/*
* Copyright 2012 University of South Florida
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.usf.cutr.opentripplanner.android.fragments;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.model.TileOverlay;
import com.google.android.gms.maps.model.TileOverlayOptions;
import org.opentripplanner.api.model.Itinerary;
import org.opentripplanner.api.model.Leg;
import org.opentripplanner.api.ws.GraphMetadata;
import org.opentripplanner.api.ws.Request;
import org.opentripplanner.index.model.TripTimeShort;
import org.opentripplanner.routing.bike_rental.BikeRentalStation;
import org.opentripplanner.routing.bike_rental.BikeRentalStationList;
import org.opentripplanner.routing.core.OptimizeType;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import android.animation.LayoutTransition;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.NotificationCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import edu.usf.cutr.opentripplanner.android.MyActivity;
import edu.usf.cutr.opentripplanner.android.OTPApp;
import edu.usf.cutr.opentripplanner.android.R;
import edu.usf.cutr.opentripplanner.android.SettingsActivity;
import edu.usf.cutr.opentripplanner.android.listeners.BikeRentalLoadCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.DateCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.MetadataRequestCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.OTPGeocodingListener;
import edu.usf.cutr.opentripplanner.android.listeners.OtpFragment;
import edu.usf.cutr.opentripplanner.android.listeners.RequestTimesForTripsCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.ServerCheckerCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.ServerSelectorCompleteListener;
import edu.usf.cutr.opentripplanner.android.listeners.TripRequestCompleteListener;
import edu.usf.cutr.opentripplanner.android.maps.CustomUrlTileProvider;
import edu.usf.cutr.opentripplanner.android.model.OTPBundle;
import edu.usf.cutr.opentripplanner.android.model.OptimizeSpinnerItem;
import edu.usf.cutr.opentripplanner.android.model.Server;
import edu.usf.cutr.opentripplanner.android.sqlite.ServersDataSource;
import edu.usf.cutr.opentripplanner.android.tasks.BikeRentalLoad;
import edu.usf.cutr.opentripplanner.android.tasks.MetadataRequest;
import edu.usf.cutr.opentripplanner.android.tasks.OTPGeocoding;
import edu.usf.cutr.opentripplanner.android.tasks.RequestTimesForTrips;
import edu.usf.cutr.opentripplanner.android.tasks.ServerChecker;
import edu.usf.cutr.opentripplanner.android.tasks.ServerSelector;
import edu.usf.cutr.opentripplanner.android.tasks.TripRequest;
import edu.usf.cutr.opentripplanner.android.util.BikeRentalStationInfo;
import edu.usf.cutr.opentripplanner.android.util.ConversionUtils;
import edu.usf.cutr.opentripplanner.android.util.CustomAddress;
import edu.usf.cutr.opentripplanner.android.util.CustomInfoWindowAdapter;
import edu.usf.cutr.opentripplanner.android.util.DateTimeDialog;
import edu.usf.cutr.opentripplanner.android.util.DirectionsGenerator;
import edu.usf.cutr.opentripplanner.android.util.LocationUtil;
import edu.usf.cutr.opentripplanner.android.util.PlacesAutoCompleteAdapter;
import edu.usf.cutr.opentripplanner.android.util.RangeSeekBar;
import edu.usf.cutr.opentripplanner.android.util.RangeSeekBar.OnRangeSeekBarChangeListener;
import edu.usf.cutr.opentripplanner.android.util.RightDrawableOnTouchListener;
import edu.usf.cutr.opentripplanner.android.util.TripInfo;
import static com.google.android.gms.location.LocationServices.API;
import static com.google.android.gms.location.LocationServices.FusedLocationApi;
/**
* Main UI screen of the mOTPApp, showing the map.
*
* @author Khoa Tran
*/
public class MainFragment extends Fragment implements
ServerSelectorCompleteListener,
TripRequestCompleteListener, MetadataRequestCompleteListener,
BikeRentalLoadCompleteListener, RequestTimesForTripsCompleteListener, OTPGeocodingListener,
DateCompleteListener, OnRangeSeekBarChangeListener<Double>, ServerCheckerCompleteListener,
GoogleApiClient.OnConnectionFailedListener,
GoogleApiClient.ConnectionCallbacks,
GoogleMap.OnCameraChangeListener {
private static LocationManager sLocationManager;
private OTPApp mOTPApp;
private Context mApplicationContext;
private GoogleApiClient mGoogleApiClient;
private OtpFragment mFragmentListener;
private SharedPreferences mPrefs;
private boolean mAppStarts = true;
private Boolean mNeedToUpdateServersList = false;
private Boolean mNeedToRunAutoDetect = false;
private ImageButton mBtnDateDialog;
private ImageButton mBtnSwapOriginDestination;
private ImageButton mBtnMyLocation;
private ImageButton mBtnHandle;
private DrawerLayout mDrawerLayout;
private ViewGroup mNavigationDrawerLeftPane;
private ListView mDdlOptimization;
private CheckBox mBtnModeBus;
private CheckBox mBtnModeTrain;
private CheckBox mBtnModeFerry;
private CheckBox mBtnModeRentedBike;
private RadioButton mBtnModeWalk;
private RadioButton mBtnModeBike;
private double mBikeTriangleMinValue = OTPApp.BIKE_PARAMETERS_QUICK_DEFAULT_VALUE;
private double mBikeTriangleMaxValue = OTPApp.BIKE_PARAMETERS_FLAT_DEFAULT_VALUE;
private RangeSeekBar<Double> mBikeTriangleParameters;
private ViewGroup mBikeTriangleParametersLayout;
private View mPanelDisplayDirection;
private Spinner mItinerarySelectionSpinner;
private ImageButton mBtnDisplayDirection;
private MenuItem mGPS;
private GoogleMap mMap;
private boolean mMapFailed;
private float mMaxZoomLevel;
private TileOverlay mSelectedTileOverlay;
private LatLng mSavedLastLocation;
private LatLng mSavedLastLocationCheckedForServer;
private Polyline mBoundariesPolyline;
private AutoCompleteTextView mTbStartLocation;
private AutoCompleteTextView mTbEndLocation;
private CustomAddress mStartAddress;
private CustomAddress mEndAddress;
private String mResultTripStartLocation;
private String mResultTripEndLocation;
private Marker mStartMarker;
private Marker mEndMarker;
private LatLng mStartMarkerPosition;
private LatLng mEndMarkerPosition;
private boolean mIsStartLocationGeocodingCompleted = true;
private boolean mIsEndLocationGeocodingCompleted = false;
private boolean mIsStartLocationChangedByUser = true;
private boolean mIsEndLocationChangedByUser = true;
private Map<Marker, TripInfo> mModeMarkers;
private List<Polyline> mRoute;
private Map<Marker, BikeRentalStationInfo> mBikeRentalStations;
private int mOptimizationValueToRestoreWhenNoBike;
private Date mTripDate;
private boolean mArriveBy;
private int mMapPaddingLeft;
private int mMapPaddingTop;
private int mMapPaddingRight;
private int mMapPaddingBottom;
private AlarmManager mAlarmMgr;
PendingIntent mAlarmIntentTripTimeUpdate;
boolean mIsAlarmTripTimeUpdateActive;
PendingIntent mAlarmIntentBikeRentalUpdate;
boolean mIsAlarmBikeRentalUpdateActive;
AlarmReceiver mAlarmReceiver;
IntentFilter mIntentFilter;
Intent mTripTimeUpdateIntent;
Intent mBikeRentalUpdateIntent;
CustomInfoWindowAdapter mCustomInfoWindowAdapter;
boolean mNewAppVersion = false;
PlacesAutoCompleteAdapter startLocationPlacesAutoCompleteAdapter;
PlacesAutoCompleteAdapter endLocationPlacesAutoCompleteAdapter;
boolean changingTextBoxWithAutocomplete = false;
private OptimizeType previousOptimization;
private TraverseModeSet previousModes;
private double previousBikeTriangleMinValue;
private double previousBikeTriangleMaxValue;
private GraphMetadata mCustomServerMetadata = null;
private OTPGeocoding mGeoCodingTask;
@SuppressWarnings("deprecation")
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static void removeOnGlobalLayoutListener(View v,
OnGlobalLayoutListener listener) {
ViewTreeObserver viewTreeObserver = v.getViewTreeObserver();
if (viewTreeObserver != null) {
if (Build.VERSION.SDK_INT < 16) {
viewTreeObserver.removeGlobalOnLayoutListener(listener);
} else {
viewTreeObserver.removeOnGlobalLayoutListener(listener);
}
} else {
Log.w(OTPApp.TAG,
"Problems obtaining exact element's positions on screen, some other elements"
+ "can be misplaced");
}
}
public void setNeedToUpdateServersList(Boolean needToUpdateServersList) {
this.mNeedToUpdateServersList = needToUpdateServersList;
}
public void setNeedToRunAutoDetect(Boolean needToRunAutoDetect) {
this.mNeedToRunAutoDetect = needToRunAutoDetect;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
((MyActivity) activity).setDateCompleteCallback(this);
setFragmentListener((OtpFragment) activity);
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OtpFragment");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
getActivity().getSupportFragmentManager()
.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
Log.i(OTPApp.TAG, "back stack changed ");
int backCount = getActivity().getSupportFragmentManager()
.getBackStackEntryCount();
if (backCount == 0) {
if (getFragmentListener() != null) {
mItinerarySelectionSpinner.setSelection(
getFragmentListener().getCurrentItineraryIndex());
}
}
}
});
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View mainView = inflater.inflate(R.layout.main, container, false);
if (mainView != null) {
ViewTreeObserver vto = mainView.getViewTreeObserver();
if (vto != null) {
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
MainFragment.removeOnGlobalLayoutListener(mainView, this);
int locationTbEndLocation[] = new int[2];
mTbEndLocation.getLocationInWindow(locationTbEndLocation);
int locationItinerarySelectionSpinner[] = new int[2];
mItinerarySelectionSpinner
.getLocationInWindow(locationItinerarySelectionSpinner);
int locationBtnHandle[] = new int[2];
mBtnHandle.getLocationInWindow(locationBtnHandle);
DisplayMetrics metrics = MainFragment.this.getResources()
.getDisplayMetrics();
int windowHeight = metrics.heightPixels;
int paddingMargin = MainFragment.this.getResources()
.getInteger(R.integer.map_padding_margin);
mMapPaddingLeft = locationBtnHandle[0] + mBtnHandle.getWidth() / 2
+ paddingMargin;
mMapPaddingTop = locationTbEndLocation[1] + mTbEndLocation.getHeight() / 2
+ paddingMargin;
mMapPaddingRight = 0;
mMapPaddingBottom = windowHeight - locationItinerarySelectionSpinner[1]
+ paddingMargin;
if (mMap != null) {
mMap.setPadding(mMapPaddingLeft, mMapPaddingTop, mMapPaddingRight, mMapPaddingBottom);
}
}
});
} else {
Log.w(OTPApp.TAG,
"Not possible to obtain exact element's positions on screen, some other"
+ "elements can be misplaced");
}
mTbStartLocation = (AutoCompleteTextView) mainView
.findViewById(R.id.tbStartLocation);
mTbEndLocation = (AutoCompleteTextView) mainView.findViewById(R.id.tbEndLocation);
mBtnSwapOriginDestination = (ImageButton) mainView.findViewById(R.id.btnSwapOriginDestination);
mDdlOptimization = (ListView) mainView
.findViewById(R.id.spinOptimization);
mBtnModeWalk = (RadioButton) mainView.findViewById(R.id.btnModeWalk);
mBtnModeBike = (RadioButton) mainView.findViewById(R.id.btnModeBike);
mBtnModeBus = (CheckBox) mainView.findViewById(R.id.btnModeBus);
mBtnModeTrain = (CheckBox) mainView.findViewById(R.id.btnModeTrain);
mBtnModeFerry = (CheckBox) mainView.findViewById(R.id.btnModeFerry);
mBtnModeRentedBike = (CheckBox) mainView.findViewById(R.id.btnModeRentedBike);
mBikeTriangleParameters = new RangeSeekBar<Double>(OTPApp.BIKE_PARAMETERS_MIN_VALUE,
OTPApp.BIKE_PARAMETERS_MAX_VALUE, this.getActivity().getApplicationContext(),
R.color.sysRed, R.color.sysGreen, R.color.sysBlue, R.drawable.seek_thumb_normal,
R.drawable.seek_thumb_pressed);
// add RangeSeekBar to pre-defined layout
mBikeTriangleParametersLayout = (ViewGroup) mainView
.findViewById(R.id.bikeParametersLayout);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, R.id.bikeParametersTags);
mBikeTriangleParametersLayout.addView(mBikeTriangleParameters, params);
mBtnMyLocation = (ImageButton) mainView.findViewById(R.id.btnMyLocation);
mBtnDateDialog = (ImageButton) mainView.findViewById(R.id.btnDateDialog);
mBtnDisplayDirection = (ImageButton) mainView
.findViewById(R.id.btnDisplayDirection);
mNavigationDrawerLeftPane = (ViewGroup) mainView
.findViewById(R.id.navigationDrawerLeftPane);
mPanelDisplayDirection = mainView.findViewById(R.id.panelDisplayDirection);
mBtnHandle = (ImageButton) mainView.findViewById(R.id.btnHandle);
mDrawerLayout = (DrawerLayout) mainView.findViewById(R.id.drawerLayout);
mTbStartLocation.setImeOptions(EditorInfo.IME_ACTION_NEXT);
mTbEndLocation.setImeOptions(EditorInfo.IME_ACTION_DONE);
mTbEndLocation.setImeActionLabel(getResources().getString(R.string.text_box_virtual_keyboard_done_label), EditorInfo.IME_ACTION_DONE);
mTbEndLocation.requestFocus();
mItinerarySelectionSpinner = (Spinner) mainView.findViewById(R.id.itinerarySelection);
Log.d(OTPApp.TAG, "finish onStart()");
if (Build.VERSION.SDK_INT > 11) {
LayoutTransition l = new LayoutTransition();
ViewGroup mainButtons = (ViewGroup) mainView
.findViewById(R.id.content_frame);
mainButtons.setLayoutTransition(l);
}
return mainView;
} else {
Log.e(OTPApp.TAG, "Not possible to obtain main view, UI won't be correctly created");
return null;
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.d(OTPApp.TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
mApplicationContext = getActivity().getApplicationContext();
mIntentFilter = new IntentFilter(OTPApp.INTENT_UPDATE_BIKE_RENTAL_ACTION);
mIntentFilter.addAction(OTPApp.INTENT_UPDATE_TRIP_TIME_ACTION);
mIntentFilter.addAction(OTPApp.INTENT_NOTIFICATION_ACTION_OPEN_APP);
mIntentFilter.addAction(OTPApp.INTENT_NOTIFICATION_ACTION_DISMISS_UPDATES);
mBikeRentalUpdateIntent = new Intent(OTPApp.INTENT_UPDATE_BIKE_RENTAL_ACTION);
mAlarmIntentBikeRentalUpdate = PendingIntent.getBroadcast(mApplicationContext, 0, mBikeRentalUpdateIntent, 0);
mTripTimeUpdateIntent = new Intent(OTPApp.INTENT_UPDATE_TRIP_TIME_ACTION);
mAlarmIntentTripTimeUpdate = PendingIntent.getBroadcast(mApplicationContext, 0, mTripTimeUpdateIntent, 0);
mAlarmMgr = (AlarmManager)mApplicationContext.getSystemService(Context.ALARM_SERVICE);
mIsAlarmBikeRentalUpdateActive = false;
mIsAlarmTripTimeUpdateActive = false;
mAlarmReceiver = new AlarmReceiver();
mMap = retrieveMap(mMap);
mOTPApp = ((OTPApp) getActivity().getApplication());
mPrefs = PreferenceManager.getDefaultSharedPreferences(
mApplicationContext);
sLocationManager = (LocationManager) getActivity()
.getSystemService(Context.LOCATION_SERVICE);
if (savedInstanceState == null) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, true);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false);
prefsEditor.commit();
}
checkAppVersion();
if (savedInstanceState == null) {
restorePanelUI();
mArriveBy = false;
setTextBoxLocation(getResources().getString(R.string.text_box_my_location), true);
}
ArrayAdapter<OptimizeSpinnerItem> optimizationAdapter
= new ArrayAdapter<OptimizeSpinnerItem>(
getActivity(),
android.R.layout.simple_list_item_single_choice,
new OptimizeSpinnerItem[]{
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_quick),
OptimizeType.QUICK),
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_safe),
OptimizeType.SAFE),
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_fewest_transfers),
OptimizeType.TRANSFERS)});
Server selectedServer = mOTPApp.getSelectedServer();
if (selectedServer != null) {
if (!mMapFailed) {
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(getServerCenter(selectedServer),
getServerInitialZoom(selectedServer)));
}
}
mCustomInfoWindowAdapter = new CustomInfoWindowAdapter(getLayoutInflater(savedInstanceState), mApplicationContext);
restoreState(savedInstanceState);
if (!mMapFailed) {
updateSelectedServer(false);
}
if (!mMapFailed) {
initializeMapInterface(mMap);
}
}
private void initializeMapInterface(GoogleMap mMap) {
UiSettings uiSettings = mMap.getUiSettings();
mMap.setMyLocationEnabled(true);
mMap.setOnCameraChangeListener(this);
uiSettings.setMyLocationButtonEnabled(false);
uiSettings.setCompassEnabled(true);
uiSettings.setAllGesturesEnabled(true);
uiSettings.setZoomControlsEnabled(false);
updateOverlay(ConversionUtils.getOverlayString(mApplicationContext));
addInterfaceListeners();
}
private void addInterfaceListeners() {
final OnMapClickListener onMapClickListener = new OnMapClickListener() {
@Override
public void onMapClick(LatLng latlng) {
InputMethodManager imm = (InputMethodManager) MainFragment.this.getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTbEndLocation.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mTbStartLocation.getWindowToken(), 0);
if (mTbStartLocation.hasFocus()) {
setMarker(true, latlng, true, true);
} else {
setMarker(false, latlng, true, true);
}
processRequestTrip();
}
};
mMap.setOnMapClickListener(onMapClickListener);
OnFocusChangeListener tbLocationOnFocusChangeListener = new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
TextView tv = (TextView) v;
mMap.setOnMapClickListener(onMapClickListener);
CharSequence tvCharSequence = tv.getText();
if (tvCharSequence != null) {
String text = tvCharSequence.toString();
if (!TextUtils.isEmpty(text)) {
if (v.getId() == R.id.tbStartLocation) {
if (!mIsStartLocationGeocodingCompleted) {
mTbStartLocation.showDropDown();
}
} else if (v.getId() == R.id.tbEndLocation) {
if (!mIsEndLocationGeocodingCompleted) {
mTbEndLocation.showDropDown();
}
}
}
} else {
Log.w(OTPApp.TAG,
"Focus has changed, but was not possible to obtain start/end"
+ " textbox text");
}
}
}
};
mTbStartLocation.setOnFocusChangeListener(tbLocationOnFocusChangeListener);
mTbEndLocation.setOnFocusChangeListener(tbLocationOnFocusChangeListener);
OnMarkerDragListener onMarkerDragListener = new OnMarkerDragListener() {
@Override
public void onMarkerDrag(Marker marker) {
}
@Override
public void onMarkerDragEnd(Marker marker) {
LatLng markerLatlng = marker.getPosition();
if (((mOTPApp.getSelectedServer() != null) && LocationUtil
.checkPointInBoundingBox(markerLatlng, mOTPApp.getSelectedServer()))
|| (mOTPApp.getSelectedServer() == null)) {
if ((mStartMarker != null) && (marker.hashCode() == mStartMarker.hashCode())) {
if (mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true)) {
updateMarkerPosition(markerLatlng, true);
} else {
mIsStartLocationGeocodingCompleted = true;
removeFocus(true);
setMarker(true, markerLatlng, false, true);
}
mStartMarkerPosition = markerLatlng;
processRequestTrip();
} else if ((mEndMarker != null) && (marker.hashCode() == mEndMarker
.hashCode())) {
if (mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true)) {
updateMarkerPosition(markerLatlng, false);
} else {
mIsEndLocationGeocodingCompleted = true;
removeFocus(false);
setMarker(false, markerLatlng, false, true);
}
mEndMarkerPosition = markerLatlng;
processRequestTrip();
}
} else {
if ((mStartMarker != null) && (marker.hashCode() == mStartMarker.hashCode())) {
marker.setPosition(mStartMarkerPosition);
} else {
marker.setPosition(mEndMarkerPosition);
}
Toast.makeText(mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_map_markers_marker_out_of_boundaries), Toast.LENGTH_SHORT)
.show();
}
}
@Override
public void onMarkerDragStart(Marker marker) {
InputMethodManager imm = (InputMethodManager) MainFragment.this.getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTbEndLocation.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mTbStartLocation.getWindowToken(), 0);
}
};
mMap.setOnMarkerDragListener(onMarkerDragListener);
OnMapLongClickListener onMapLongClickListener = new OnMapLongClickListener() {
@Override
public void onMapLongClick(LatLng latlng) {
InputMethodManager imm = (InputMethodManager) MainFragment.this.getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTbEndLocation.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mTbStartLocation.getWindowToken(), 0);
final LatLng latLngFinal = latlng;
final CharSequence[] items = {mApplicationContext.getResources()
.getString(R.string.point_type_selector_start_marker_option),
mApplicationContext.getResources()
.getString(R.string.point_type_selector_end_marker_option)};
AlertDialog.Builder builder = new AlertDialog.Builder(
MainFragment.this.getActivity());
builder.setTitle(getResources().getString(R.string.point_type_selector_title));
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
setMarker(true, latLngFinal, true, true);
} else {
setMarker(false, latLngFinal, true, true);
}
processRequestTrip();
}
});
AlertDialog alert = builder.create();
alert.show();
}
};
mMap.setOnMapLongClickListener(onMapLongClickListener);
OnClickListener onClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mDrawerLayout.openDrawer(Gravity.LEFT);
}
};
mBtnHandle.setOnClickListener(onClickListener);
OnInfoWindowClickListener onInfoWindowClickListener = new OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
if (mBikeRentalStations != null && mBikeRentalStations.containsKey(marker)){
BikeRentalStationInfo bikeRentalStationInfo = mBikeRentalStations.get(marker);
setMarker(true, bikeRentalStationInfo.getLocation(), false, false);
setTextBoxLocation(bikeRentalStationInfo.getName(), true);
}
saveOTPBundle();
OTPBundle otpBundle = getFragmentListener().getOTPBundle();
Matcher matcher = Pattern.compile("\\d+").matcher(marker.getTitle());
if (matcher.find()) {
String numberString = marker.getTitle().substring(0, matcher.end());
//Step indexes shown to the user are in a scale starting by 1 but instructions steps internally start by 0
int currentStepIndex = Integer.parseInt(numberString) - 1;
otpBundle.setCurrentStepIndex(currentStepIndex);
otpBundle.setFromInfoWindow(true);
getFragmentListener().setOTPBundle(otpBundle);
getFragmentListener().onSwitchedToDirectionFragment();
}
}
};
mMap.setOnInfoWindowClickListener(onInfoWindowClickListener);
DrawerListener dl = new DrawerListener() {
@Override
public void onDrawerStateChanged(int arg0) {
}
@Override
public void onDrawerSlide(View arg0, float arg1) {
InputMethodManager imm = (InputMethodManager) MainFragment.this.getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTbEndLocation.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mTbStartLocation.getWindowToken(), 0);
}
@Override
public void onDrawerOpened(View arg0) {
previousOptimization = ((OptimizeSpinnerItem) mDdlOptimization
.getItemAtPosition(mDdlOptimization.getCheckedItemPosition())).getOptimizeType();
previousModes = getSelectedTraverseModeSet();
previousBikeTriangleMinValue = mBikeTriangleMinValue;
previousBikeTriangleMaxValue = mBikeTriangleMaxValue;
}
@Override
public void onDrawerClosed(View arg0) {
TraverseModeSet newModes = getSelectedTraverseModeSet();
OptimizeType newOptimization = ((OptimizeSpinnerItem) mDdlOptimization
.getItemAtPosition(mDdlOptimization.getCheckedItemPosition())).getOptimizeType();
boolean sameOptimization = previousOptimization.equals(newOptimization);
boolean sameTraverseMode = previousModes.getModes().equals(newModes.getModes());
boolean sameBikeTriangle = previousBikeTriangleMinValue == mBikeTriangleMinValue
&& previousBikeTriangleMaxValue == mBikeTriangleMaxValue;
if (!sameOptimization || !sameTraverseMode || !sameBikeTriangle){
processRequestTrip();
}
}
};
mDrawerLayout.setDrawerListener(dl);
startLocationPlacesAutoCompleteAdapter = new PlacesAutoCompleteAdapter(getActivity(), android.R.layout.simple_spinner_dropdown_item,
mOTPApp.getSelectedServer());
mTbStartLocation.setAdapter(startLocationPlacesAutoCompleteAdapter);
mTbStartLocation.setThreshold(3);
endLocationPlacesAutoCompleteAdapter = new PlacesAutoCompleteAdapter(getActivity(), android.R.layout.simple_spinner_dropdown_item,
mOTPApp.getSelectedServer());
mTbStartLocation.setAdapter(startLocationPlacesAutoCompleteAdapter);
mTbEndLocation.setAdapter(endLocationPlacesAutoCompleteAdapter);
mTbEndLocation .setThreshold(3);
OnTouchListener otlStart = new RightDrawableOnTouchListener(mTbStartLocation) {
@Override
public boolean onDrawableTouch(final MotionEvent event) {
final CharSequence[] items = {
getResources().getString(R.string.text_box_dialog_location_type_current_location),
getResources().getString(R.string.text_box_dialog_location_type_contact),
getResources().getString(R.string.text_box_dialog_location_type_map_point)};
AlertDialog.Builder builder = new AlertDialog.Builder(
MainFragment.this.getActivity());
builder.setTitle(getResources().getString(R.string.text_box_dialog_choose_location_type_start));
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (items[item].equals(getResources()
.getString(R.string.text_box_dialog_location_type_current_location))) {
LatLng mCurrentLatLng = getLastLocation();
if (mCurrentLatLng != null) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
setTextBoxLocation(getResources().getString(R.string.text_box_my_location),
true);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION,
true);
if (mStartMarker != null) {
mStartMarker.remove();
mStartMarker = null;
}
prefsEditor.commit();
mIsStartLocationGeocodingCompleted = true;
processRequestTrip();
} else {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
}
} else if (items[item]
.equals(getResources().getString(R.string.text_box_dialog_location_type_contact))) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_TYPE);
((MyActivity) MainFragment.this.getActivity())
.setButtonStartLocation(true);
MainFragment.this.getActivity()
.startActivityForResult(intent,
OTPApp.CHOOSE_CONTACT_REQUEST_CODE);
} else { // Point on Map
if (mStartMarker != null) {
updateMarkerPosition(mStartMarker.getPosition(), true);
} else {
setTextBoxLocation("", true);
mTbStartLocation.setHint(
getResources().getString(R.string.text_box_need_to_place_marker));
mTbStartLocation.requestFocus();
}
}
}
});
AlertDialog alert = builder.create();
alert.show();
return true;
}
};
mTbStartLocation.setOnTouchListener(otlStart);
OnTouchListener otlEnd = new RightDrawableOnTouchListener(mTbEndLocation) {
@Override
public boolean onDrawableTouch(final MotionEvent event) {
final CharSequence[] items = {
getResources().getString(R.string.text_box_dialog_location_type_current_location),
getResources().getString(R.string.text_box_dialog_location_type_contact),
getResources().getString(R.string.text_box_dialog_location_type_map_point)};
AlertDialog.Builder builder = new AlertDialog.Builder(
MainFragment.this.getActivity());
builder.setTitle(getResources().getString(R.string.text_box_dialog_choose_location_type_end));
builder.setItems(items, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if (items[item].equals(getResources()
.getString(R.string.text_box_dialog_location_type_current_location))) {
LatLng mCurrentLatLng = getLastLocation();
if (mCurrentLatLng != null) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
setTextBoxLocation(getResources().getString(R.string.text_box_my_location),
false);
prefsEditor.putBoolean(
OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, true);
if (mEndMarker != null) {
mEndMarker.remove();
mEndMarker = null;
}
prefsEditor.commit();
mIsEndLocationGeocodingCompleted = true;
processRequestTrip();
} else {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
}
} else if (items[item]
.equals(getResources().getString(R.string.text_box_dialog_location_type_contact))) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_TYPE);
((MyActivity) MainFragment.this.getActivity())
.setButtonStartLocation(false);
MainFragment.this.getActivity()
.startActivityForResult(intent,
OTPApp.CHOOSE_CONTACT_REQUEST_CODE);
} else { // Point on Map
if (mEndMarker != null) {
updateMarkerPosition(mEndMarker.getPosition(), false);
} else {
setTextBoxLocation("", false);
mTbEndLocation.setHint(
getResources().getString(R.string.text_box_need_to_place_marker));
mTbEndLocation.requestFocus();
}
}
}
});
AlertDialog alert = builder.create();
alert.show();
return true;
}
};
mTbEndLocation.setOnTouchListener(otlEnd);
OnItemClickListener tbAutocompleteOnItemClickListener = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
changingTextBoxWithAutocomplete = true;
boolean isStartBox = mTbStartLocation.hasFocus();
CustomAddress selectedAddress = (CustomAddress) adapterView.getItemAtPosition(position);
useNewAddress(isStartBox, selectedAddress, false);
}
};
mTbStartLocation.setOnItemClickListener(tbAutocompleteOnItemClickListener);
mTbEndLocation.setOnItemClickListener(tbAutocompleteOnItemClickListener);
TextWatcher textWatcherStart = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
if (!changingTextBoxWithAutocomplete){
if (mIsStartLocationChangedByUser) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, false);
prefsEditor.commit();
mIsStartLocationGeocodingCompleted = false;
} else {
mIsStartLocationChangedByUser = true;
}
}
}
};
TextWatcher textWatcherEnd = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
if (!changingTextBoxWithAutocomplete) {
if (mIsEndLocationChangedByUser) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false);
prefsEditor.commit();
mIsEndLocationGeocodingCompleted = false;
} else {
mIsEndLocationChangedByUser = true;
}
}
}
};
mTbStartLocation.addTextChangedListener(textWatcherStart);
mTbEndLocation.addTextChangedListener(textWatcherEnd);
OnEditorActionListener tbLocationOnEditorActionListener = new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (v.getId() == R.id.tbStartLocation) {
if (actionId == EditorInfo.IME_ACTION_NEXT
|| (event != null
&& event.getAction() == KeyEvent.ACTION_DOWN && event
.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if (!mIsStartLocationGeocodingCompleted
&& !mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, true)) {
CharSequence tvCharSequence = v.getText();
if (tvCharSequence != null) {
processAddress(true, tvCharSequence.toString(), false);
} else {
Log.w(OTPApp.TAG,
"User switched to next input, but was not possible to"
+ "obtain start/end textbox text");
}
}
}
} else if (v.getId() == R.id.tbEndLocation) {
if (actionId == EditorInfo.IME_ACTION_DONE
|| (event != null
&& event.getAction() == KeyEvent.ACTION_DOWN && event
.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if (!mIsEndLocationGeocodingCompleted
&& !mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, true)) {
CharSequence tvCharSequence = v.getText();
if (tvCharSequence != null) {
processAddress(false, tvCharSequence.toString(), false);
} else {
Log.w(OTPApp.TAG,
"User pressed done, but was not possible to"
+ "obtain start/end textbox text");
}
}
processRequestTrip();
}
}
return false;
}
};
mTbStartLocation
.setOnEditorActionListener(tbLocationOnEditorActionListener);
mTbEndLocation
.setOnEditorActionListener(tbLocationOnEditorActionListener);
OnClickListener oclDisplayDirection = new OnClickListener() {
@Override
public void onClick(View arg0) {
saveOTPBundle();
getFragmentListener().onSwitchedToDirectionFragment();
}
};
mBtnDisplayDirection.setOnClickListener(oclDisplayDirection);
// Do NOT show direction icon if there is no direction yet
toggleItinerarySelectionSpinner(!getFragmentListener().getCurrentItinerary().isEmpty());
OnClickListener oclMyLocation = new OnClickListener() {
@Override
public void onClick(View arg0) {
LatLng mCurrentLatLng = getLastLocation();
if (mCurrentLatLng == null) {
Toast.makeText(mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
} else {
if (!mMapFailed){
if (mMap.getCameraPosition().zoom < OTPApp.defaultMyLocationZoomLevel) {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(mCurrentLatLng, OTPApp.defaultMyLocationZoomLevel));
} else {
mMap.animateCamera(CameraUpdateFactory.newLatLng(getLastLocation()));
}
}
}
}
};
mBtnMyLocation.setOnClickListener(oclMyLocation);
OnClickListener oclDateDialog = new OnClickListener() {
@Override
public void onClick(View arg0) {
FragmentTransaction ft = MainFragment.this.getActivity().getSupportFragmentManager()
.beginTransaction();
Fragment prev = MainFragment.this.getActivity().getSupportFragmentManager()
.findFragmentByTag(OTPApp.TAG_FRAGMENT_DATE_TIME_DIALOG);
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DateTimeDialog newFragment = new DateTimeDialog();
Date dateDialogDate;
if (mTripDate == null) {
dateDialogDate = Calendar.getInstance().getTime();
} else {
dateDialogDate = mTripDate;
}
Bundle bundle = new Bundle();
bundle.putSerializable(OTPApp.BUNDLE_KEY_TRIP_DATE, dateDialogDate);
bundle.putBoolean(OTPApp.BUNDLE_KEY_ARRIVE_BY, mArriveBy);
newFragment.setArguments(bundle);
ft.commit();
newFragment.show(MainFragment.this.getActivity().getSupportFragmentManager(),
OTPApp.TAG_FRAGMENT_DATE_TIME_DIALOG);
}
};
mBtnDateDialog.setOnClickListener(oclDateDialog);
OnClickListener oclSwapOriginDestination = new OnClickListener() {
@Override
public void onClick(View arg0) {
if (!mMapFailed){
boolean tempBoolean;
CustomAddress tempAddress;
tempBoolean = mIsStartLocationGeocodingCompleted;
mIsStartLocationGeocodingCompleted = mIsEndLocationGeocodingCompleted;
mIsEndLocationGeocodingCompleted = tempBoolean;
tempBoolean = mIsStartLocationChangedByUser;
mIsStartLocationChangedByUser = mIsEndLocationChangedByUser;
mIsEndLocationChangedByUser = tempBoolean;
tempAddress = mStartAddress;
mStartAddress = mEndAddress;
mEndAddress = tempAddress;
SharedPreferences.Editor prefsEditor = mPrefs.edit();
Boolean tempPref = mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, false);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false));
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, tempPref);
prefsEditor.commit();
Marker mOldStartMarker = mStartMarker;
MarkerOptions newStartMarkerOptions = new MarkerOptions();
MarkerOptions newEndMarkerOptions = new MarkerOptions();
if (mEndMarker != null) {
newStartMarkerOptions.snippet(mEndMarker.getSnippet());
newStartMarkerOptions.title(mEndMarker.getTitle());
newStartMarkerOptions.position(mEndMarker.getPosition());
newStartMarkerOptions.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
}
if (mStartMarker != null) {
mStartMarker.remove();
}
if (mEndMarker != null){
mStartMarker = mMap.addMarker(newStartMarkerOptions);
mStartMarkerPosition = mStartMarker.getPosition();
}
else{
mStartMarker = null;
mStartMarkerPosition = null;
}
if (mOldStartMarker != null) {
newEndMarkerOptions.snippet(mOldStartMarker.getSnippet());
newEndMarkerOptions.title(mOldStartMarker.getTitle());
newEndMarkerOptions.position(mOldStartMarker.getPosition());
newEndMarkerOptions.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_RED));
}
if (mEndMarker != null) {
mEndMarker.remove();
}
if (mOldStartMarker != null){
mEndMarker = mMap.addMarker(newEndMarkerOptions);
mEndMarkerPosition = mEndMarker.getPosition();
}
else{
mEndMarker = null;
mEndMarkerPosition = null;
}
CharSequence tempCharSequence = mTbStartLocation.getText();
setTextBoxLocation(mTbEndLocation.getText().toString(), true);
setTextBoxLocation(tempCharSequence.toString(), false);
if (TextUtils.isEmpty(mTbStartLocation.getText())) {
mTbStartLocation.setHint(getResources()
.getString(R.string.text_box_start_location_hint));
}
if (TextUtils.isEmpty(mTbEndLocation.getText())) {
mTbEndLocation.setHint(getResources()
.getString(R.string.text_box_end_location_hint));
}
processRequestTrip();
}
}
};
mBtnSwapOriginDestination.setOnClickListener(oclSwapOriginDestination);
AdapterView.OnItemSelectedListener itinerarySpinnerListener
= new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (mFragmentListener.getCurrentItinerary() != null){
if (mFragmentListener.getCurrentItineraryIndex() != position) {
mFragmentListener.onItinerarySelected(position, 2);
}
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
};
int currentItineraryIndex = mFragmentListener.getCurrentItineraryIndex();
mItinerarySelectionSpinner.setSelection(currentItineraryIndex);
mItinerarySelectionSpinner.setOnItemSelectedListener(itinerarySpinnerListener);
mBikeTriangleParameters
.setOnRangeSeekBarChangeListener(new OnRangeSeekBarChangeListener<Double>() {
@Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> rangeSeekBar,
Double minValue, Double maxValue) {
// handle changed range values
Log.i(OTPApp.TAG,
"User selected new range values: MIN=" + minValue + ", MAX="
+ maxValue);
}
});
mBtnModeWalk.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateModes(getSelectedTraverseModeSet());
}
});
mBtnModeBike.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setBikeOptimizationAdapter(isChecked, true);
showBikeParameters(isChecked);
mBtnModeRentedBike.setEnabled(!isChecked);
updateModes(getSelectedTraverseModeSet());
}
});
mBtnModeRentedBike.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setBikeOptimizationAdapter(isChecked, true);
showBikeParameters(isChecked);
mBtnModeBike.setEnabled(!isChecked);
updateModes(getSelectedTraverseModeSet());
if (mOTPApp.getSelectedServer().getOffersBikeRental() && isChecked){
BikeRentalLoad bikeRentalGetStations = new BikeRentalLoad(mApplicationContext, true,
MainFragment.this);
bikeRentalGetStations.execute(mOTPApp.getSelectedServer().getBaseURL());
}
if (mBikeRentalStations != null && !isChecked){
removeBikeStations();
}
}
});
mBtnModeBus.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateModes(getSelectedTraverseModeSet());
}
});
mBtnModeTrain.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateModes(getSelectedTraverseModeSet());
}
});
mBtnModeFerry.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
updateModes(getSelectedTraverseModeSet());
}
});
mDdlOptimization.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (getSelectedTraverseModeSet().getBicycle()) {
mOptimizationValueToRestoreWhenNoBike = position;
}
SharedPreferences.Editor editor = mPrefs.edit();
editor.putInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, position);
editor.commit();
OptimizeSpinnerItem optimizeSpinnerItem = (OptimizeSpinnerItem) mDdlOptimization
.getItemAtPosition(position);
if (optimizeSpinnerItem != null) {
showBikeParameters(
optimizeSpinnerItem.getOptimizeType().equals(OptimizeType.TRIANGLE));
} else {
Log.e(OTPApp.TAG, "Not possible to change optimization mode because selected"
+ "optimization is unknown");
}
}
});
mBikeTriangleParameters.setOnRangeSeekBarChangeListener(this);
}
/**
* Wrapper to call the functions to recover trip options (traverse mode, optimization type and
* bike parameters) to last state, before closing the app, if there isn't data functions set
* options to default value. If it's needed UI update is also processed.
*/
private void restorePanelUI(){
restoreTraverseModes();
restoreBikeParameters();
restoreOptimization();
}
/**
* Recovers trip optimization value to last state, before closing the app, if there isn't data
* sets default value.
*/
private void restoreOptimization(){
int previousOptimization;
if ((previousOptimization =
mPrefs.getInt(mOTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, -1)) != -1){
mDdlOptimization.setItemChecked(previousOptimization, true);
}
else{
SharedPreferences.Editor editor = mPrefs.edit();
editor.putInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, 0);
editor.commit();
mDdlOptimization.setItemChecked(0, true);
}
}
/**
* Recovers bike parameters to last state, before closing the app, if there isn't data sets
* default value.
*/
private void restoreBikeParameters(){
double previousMinValue, previousMaxValue;
if ((previousMinValue = mPrefs.getFloat(OTPApp.PREFERENCE_KEY_LAST_BIKE_TRIANGLE_MIN_VALUE, -1)) != -1){
mBikeTriangleParameters.setSelectedMinValue(previousMinValue);
}
else {
mBikeTriangleParameters.setSelectedMinValue(OTPApp.BIKE_PARAMETERS_QUICK_DEFAULT_VALUE);
}
if ((previousMaxValue = mPrefs.getFloat(OTPApp.PREFERENCE_KEY_LAST_BIKE_TRIANGLE_MAX_VALUE, -1)) != -1){
mBikeTriangleParameters.setSelectedMaxValue(previousMaxValue);
}
else{
mBikeTriangleParameters.setSelectedMaxValue(OTPApp.BIKE_PARAMETERS_FLAT_DEFAULT_VALUE);
}
}
/**
* Recovers trip traverse mode to last state, before closing the app, if there isn't data
* sets default value. It also updates UI according to the new modes.
*/
private void restoreTraverseModes(){
String lastTraverseModeSet;
if ((lastTraverseModeSet =
mPrefs.getString(OTPApp.PREFERENCE_KEY_LAST_TRAVERSE_MODE_SET, "")).equals("")){
mBtnModeWalk.setChecked(true);
mBtnModeBus.setChecked(true);
mBtnModeTrain.setChecked(true);
mBtnModeFerry.setChecked(true);
showBikeParameters(false);
setBikeOptimizationAdapter(false, false);
}
else {
TraverseModeSet traverseModeSet = new TraverseModeSet(lastTraverseModeSet);
if (traverseModeSet != null) {
mBtnModeWalk.setChecked(traverseModeSet.getWalk());
mBtnModeBus.setChecked(traverseModeSet.getBus());
mBtnModeTrain.setChecked(traverseModeSet.getRail());
mBtnModeFerry.setChecked(traverseModeSet.getFerry());
if (traverseModeSet.getWalk() && traverseModeSet.getBicycle()) {
mBtnModeRentedBike.setEnabled(true);
mBtnModeRentedBike.setChecked(true);
mBtnModeBike.setEnabled(false);
} else if (!traverseModeSet.getWalk() && traverseModeSet.getBicycle()) {
mBtnModeBike.setEnabled(true);
mBtnModeBike.setChecked(true);
mBtnModeRentedBike.setEnabled(false);
} else {
mBtnModeBike.setEnabled(true);
mBtnModeRentedBike.setEnabled(true);
setBikeOptimizationAdapter(false, false);
showBikeParameters(false);
}
if (traverseModeSet.getBicycle()){
setBikeOptimizationAdapter(true, false);
showBikeParameters(mPrefs.getInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, -1) == 2);
}
} else {
Log.e(OTPApp.TAG, "Traverse mode set is null, UI not updated");
}
}
}
private TraverseModeSet getSelectedTraverseModeSet(){
TraverseModeSet selectedTraverseModeSet = new TraverseModeSet();
selectedTraverseModeSet.setWalk(mBtnModeWalk.isChecked()
|| mBtnModeRentedBike.isChecked());
selectedTraverseModeSet.setBicycle(mBtnModeBike.isChecked()
|| mBtnModeRentedBike.isChecked());
selectedTraverseModeSet.setBus(mBtnModeBus.isChecked());
selectedTraverseModeSet.setTrainish(mBtnModeTrain.isChecked());
selectedTraverseModeSet.setFerry(mBtnModeFerry.isChecked());
return selectedTraverseModeSet;
}
private void updateModes(TraverseModeSet modeSet){
SharedPreferences.Editor editor = mPrefs.edit();
String modesString = modeSet.toString();
editor.putString(OTPApp.PREFERENCE_KEY_LAST_TRAVERSE_MODE_SET, modesString);
editor.commit();
}
/**
* Wrapper to call request trip, triggering geocoding processes if it's
* necessary.
*/
public void processRequestTrip() {
if (mIsStartLocationGeocodingCompleted && mIsEndLocationGeocodingCompleted){
requestTrip();
}
}
/**
* Sends information of the text boxes to fragment listener class through a
* bundle.
* <p>
* Fragment listener provides intercommunication with other fragments or classes.
*/
private void saveOTPBundle() {
OTPBundle bundle = new OTPBundle();
bundle.setFromText(mResultTripStartLocation);
bundle.setToText(mResultTripEndLocation);
this.getFragmentListener().setOTPBundle(bundle);
}
private void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mMap = retrieveMap(mMap);
if (!mMapFailed) {
boolean mapFailedBefore = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY_MAP_FAILED);
if (mapFailedBefore) {
enableUIElements(true);
initializeMapInterface(mMap);
}
if (!mapFailedBefore) {
updateOverlay(ConversionUtils.getOverlayString(mApplicationContext));
}
setTextBoxLocation(
savedInstanceState.getString(OTPApp.BUNDLE_KEY_TB_START_LOCATION), true);
setTextBoxLocation(savedInstanceState.getString(OTPApp.BUNDLE_KEY_TB_END_LOCATION),
false);
CameraPosition camPosition = savedInstanceState
.getParcelable(OTPApp.BUNDLE_KEY_MAP_CAMERA);
if (camPosition != null) {
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(camPosition));
}
if ((mStartMarkerPosition = savedInstanceState
.getParcelable(OTPApp.BUNDLE_KEY_MAP_START_MARKER_POSITION)) != null) {
mStartMarker = addStartEndMarker(mStartMarkerPosition, true);
}
if ((mEndMarkerPosition = savedInstanceState
.getParcelable(OTPApp.BUNDLE_KEY_MAP_END_MARKER_POSITION)) != null) {
mEndMarker = addStartEndMarker(mEndMarkerPosition, false);
}
mIsStartLocationGeocodingCompleted = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY_IS_START_LOCATION_GEOCODING_PROCESSED);
mIsEndLocationGeocodingCompleted = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY_IS_END_LOCATION_GEOCODING_PROCESSED);
mAppStarts = savedInstanceState.getBoolean(OTPApp.BUNDLE_KEY_APP_STARTS);
mIsStartLocationChangedByUser = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY_IS_START_LOCATION_CHANGED_BY_USER);
mIsEndLocationChangedByUser = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY_IS_END_LOCATION_CHANGED_BY_USER);
mSavedLastLocation = savedInstanceState
.getParcelable(OTPApp.BUNDLE_KEY_SAVED_LAST_LOCATION);
mSavedLastLocationCheckedForServer = savedInstanceState
.getParcelable(OTPApp.BUNDLE_KEY_SAVED_LAST_LOCATION_CHECKED_FOR_SERVER);
restorePanelUI();
OTPBundle otpBundle = (OTPBundle) savedInstanceState
.getSerializable(OTPApp.BUNDLE_KEY_OTP_BUNDLE);
if (otpBundle != null) {
List<Itinerary> itineraries = otpBundle.getItineraryList();
getFragmentListener().onItinerariesLoaded(itineraries);
getFragmentListener().onItinerarySelected(otpBundle.getCurrentItineraryIndex(), 0);
fillItinerariesSpinner(itineraries);
}
Date savedTripDate = (Date) savedInstanceState
.getSerializable(OTPApp.BUNDLE_KEY_TRIP_DATE);
if (savedTripDate != null) {
mTripDate = savedTripDate;
}
mArriveBy = savedInstanceState.getBoolean(OTPApp.BUNDLE_KEY_ARRIVE_BY, false);
if (savedInstanceState.getString(OTPApp.BUNDLE_KEY_RESULT_TRIP_START_LOCATION)
!= null) {
mResultTripStartLocation = savedInstanceState
.getString(OTPApp.BUNDLE_KEY_RESULT_TRIP_START_LOCATION);
}
if (savedInstanceState.getString(OTPApp.BUNDLE_KEY_RESULT_TRIP_END_LOCATION)
!= null) {
mResultTripEndLocation = savedInstanceState
.getString(OTPApp.BUNDLE_KEY_RESULT_TRIP_END_LOCATION);
}
mIsStartLocationChangedByUser = false;
mIsEndLocationChangedByUser = false;
mIsAlarmBikeRentalUpdateActive = savedInstanceState
.getBoolean(OTPApp.BUNDLE_KEY__IS_ALARM_BIKE_RENTAL_ACTIVE, false);
previousOptimization = (OptimizeType) savedInstanceState
.getSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_OPTIMIZATION);
previousModes = (TraverseModeSet) savedInstanceState
.getSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_MODES);
previousBikeTriangleMinValue = savedInstanceState
.getDouble(OTPApp.BUNDLE_KEY_PREVIOUS_BIKE_TRIANGLE_MIN_VALUE);
previousBikeTriangleMaxValue = savedInstanceState
.getDouble(OTPApp.BUNDLE_KEY_PREVIOUS_BIKE_TRIANGLE_MAX_VALUE);
mCustomServerMetadata = (GraphMetadata) savedInstanceState
.getSerializable(OTPApp.BUNDLE_KEY_CUSTOM_SERVER_METADATA);
}
}
}
/**
* Activates/deactivates all the UI, avoiding to take care of the possible
* listeners functions if the application is in a non working state.
*
* @param enable if true elements will be enabled
*/
private void enableUIElements(boolean enable) {
int visibility;
if (enable) {
setHasOptionsMenu(true);
visibility = View.VISIBLE;
} else {
setHasOptionsMenu(false);
visibility = View.INVISIBLE;
}
mTbStartLocation.setVisibility(visibility);
mTbEndLocation.setVisibility(visibility);
mBtnDateDialog.setVisibility(visibility);
mBtnMyLocation.setVisibility(visibility);
mNavigationDrawerLeftPane.setVisibility(visibility);
toggleItinerarySelectionSpinner(enable);
}
/**
* Shows/hides itinerary drop down list of map main view.
* <p>
* Moves related buttons for MyLocation and the handle to show the left
* panel accordingly.
*
* @param show if true drop down list will be shown
*/
private void toggleItinerarySelectionSpinner(boolean show) {
RelativeLayout.LayoutParams paramsMyLocation
= (RelativeLayout.LayoutParams) mBtnMyLocation.getLayoutParams();
RelativeLayout.LayoutParams paramsHandle
= (RelativeLayout.LayoutParams) mBtnHandle.getLayoutParams();
if ((paramsHandle != null) && (paramsMyLocation != null)) {
if (show) {
mPanelDisplayDirection.setVisibility(View.VISIBLE);
//Workaround, this value proves to be false, but is dirty. This is because removeRule is not defined in early versions of the API
paramsMyLocation.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0);
//Workaround, this value proves to be false, but is dirty. This is because removeRule is not defined in early versions of the API
paramsHandle.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0);
} else {
mPanelDisplayDirection.setVisibility(View.INVISIBLE);
paramsMyLocation.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
paramsHandle.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
}
mBtnMyLocation.setLayoutParams(paramsMyLocation);
mBtnHandle.setLayoutParams(paramsHandle);
mBtnMyLocation.requestLayout();
mBtnHandle.requestLayout();
} else {
Log.w(OTPApp.TAG, "Not possible to move down itineraries spinner");
}
}
private void requestTrip() {
LatLng mCurrentLatLng = getLastLocation();
String startLocationString;
String endLocationString;
Boolean isOriginMyLocation = mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, false);
Boolean isDestinationMyLocation = mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false);
toggleItinerarySelectionSpinner(false);
if (mRoute != null) {
for (Polyline p : mRoute) {
p.remove();
}
mRoute = null;
}
if (mModeMarkers != null) {
for (Map.Entry<Marker, TripInfo> entry : mModeMarkers.entrySet()) {
entry.getKey().remove();
}
mModeMarkers = null;
}
if (isOriginMyLocation && isDestinationMyLocation) {
Toast.makeText(MainFragment.this.mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_origin_destination_are_equal), Toast.LENGTH_SHORT)
.show();
return;
} else if (isOriginMyLocation || isDestinationMyLocation) {
if (mCurrentLatLng == null) {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
return;
} else {
if (isOriginMyLocation) {
startLocationString = mCurrentLatLng.latitude + "," + mCurrentLatLng.longitude;
if (mEndMarker == null) {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_need_to_place_markers_before_planning),
Toast.LENGTH_SHORT).show();
return;
} else {
endLocationString = mEndMarker.getPosition().latitude + "," + mEndMarker
.getPosition().longitude;
}
} else {
endLocationString = mCurrentLatLng.latitude + "," + mCurrentLatLng.longitude;
if (mStartMarker == null) {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_need_to_place_markers_before_planning),
Toast.LENGTH_SHORT).show();
return;
} else {
startLocationString = mStartMarker.getPosition().latitude + ","
+ mStartMarker
.getPosition().longitude;
}
}
}
} else {
if ((mStartMarker == null) || (mEndMarker == null)) {
Toast.makeText(MainFragment.this.mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_need_to_place_markers_before_planning),
Toast.LENGTH_SHORT).show();
return;
} else {
startLocationString = mStartMarker.getPosition().latitude + "," + mStartMarker
.getPosition().longitude;
endLocationString = mEndMarker.getPosition().latitude + "," + mEndMarker
.getPosition().longitude;
if (startLocationString.equals(endLocationString)){
Toast.makeText(MainFragment.this.mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_origin_destination_are_equal), Toast.LENGTH_SHORT)
.show();
return;
}
}
}
if (!mIsStartLocationGeocodingCompleted && !isOriginMyLocation) {
Toast.makeText(MainFragment.this.mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_need_to_place_markers_before_planning), Toast.LENGTH_SHORT)
.show();
return;
} else if (!mIsEndLocationGeocodingCompleted && !isDestinationMyLocation) {
Toast.makeText(MainFragment.this.mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_need_to_place_markers_before_planning), Toast.LENGTH_SHORT)
.show();
return;
}
Request request = new Request();
try {
request.setFrom(URLEncoder.encode(startLocationString, OTPApp.URL_ENCODING));
request.setTo(URLEncoder.encode(endLocationString, OTPApp.URL_ENCODING));
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
request.setArriveBy(mArriveBy);
OptimizeSpinnerItem optimizeSpinnerItem = (OptimizeSpinnerItem) mDdlOptimization
.getItemAtPosition(mDdlOptimization.getCheckedItemPosition());
if (optimizeSpinnerItem == null) {
optimizeSpinnerItem = (OptimizeSpinnerItem) mDdlOptimization.getItemAtPosition(0);
}
if (optimizeSpinnerItem != null) {
request.setOptimize(optimizeSpinnerItem.getOptimizeType());
if (optimizeSpinnerItem.getOptimizeType().equals(OptimizeType.TRIANGLE)) {
request.setTriangleTimeFactor(mBikeTriangleMinValue);
request.setTriangleSlopeFactor(mBikeTriangleMaxValue - mBikeTriangleMinValue);
request.setTriangleSafetyFactor(1 - mBikeTriangleMaxValue);
}
} else {
Log.e(OTPApp.TAG,
"Optimization not found, not possible to add it to the request so, most"
+ "likely results will be incorrect");
}
request.setModes(getSelectedTraverseModeSet());
Server selectedServer = mOTPApp.getSelectedServer();
if (selectedServer != null && selectedServer.getOffersBikeRental()){
if (mBtnModeRentedBike.isChecked()){
request.setBikeRental(true);
}
}
Integer defaultMaxWalkInt = mApplicationContext.getResources()
.getInteger(R.integer.max_walking_distance);
try {
Double maxWalk = Double
.parseDouble(mPrefs.getString(OTPApp.PREFERENCE_KEY_MAX_WALKING_DISTANCE,
defaultMaxWalkInt.toString()));
request.setMaxWalkDistance(maxWalk);
} catch (NumberFormatException ex) {
request.setMaxWalkDistance((double) defaultMaxWalkInt);
}
request.setWheelchair(mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_WHEEL_ACCESSIBLE,
false));
Date requestTripDate;
if (mTripDate == null) {
requestTripDate = Calendar.getInstance().getTime();
} else {
requestTripDate = mTripDate;
}
request.setDateTime(
DateFormat.format(OTPApp.FORMAT_OTP_SERVER_DATE_QUERY,
requestTripDate.getTime()).toString(),
DateFormat
.format(OTPApp.FORMAT_OTP_SERVER_TIME_QUERY, requestTripDate.getTime())
.toString());
request.setShowIntermediateStops(Boolean.TRUE);
WeakReference<Activity> weakContext = new WeakReference<Activity>(
MainFragment.this.getActivity());
new TripRequest(weakContext, MainFragment.this.mApplicationContext, getResources(), mOTPApp
.getSelectedServer(), MainFragment.this)
.execute(request);
InputMethodManager imm = (InputMethodManager) MainFragment.this.getActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mTbEndLocation.getWindowToken(), 0);
imm.hideSoftInputFromWindow(mTbStartLocation.getWindowToken(), 0);
mTripDate = null;
}
/**
* Retrieves a map if the map fragment parameter is null.
* <p>
* If there is an error tries to solve it checking if it was because of
* "Google Play Services" sending the corresponding intent.
*
* @param mMap map fragment to check if the map is already initialized
* @return initialized map fragment
*/
private GoogleMap retrieveMap(GoogleMap mMap) {
// Do a null check to confirm that we have not already instantiated the map.
mMapFailed = false;
if (mMap == null) {
mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
.getMap();
// Check if we were successful in obtaining the map.
if (mMap == null) {
int status = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(mApplicationContext);
if (status != ConnectionResult.SUCCESS) {
enableUIElements(false);
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(status, getActivity(),
OTPApp.CHECK_GOOGLE_PLAY_REQUEST_CODE);
dialog.show();
mMapFailed = true;
}
}
}
return mMap;
}
/**
* Wrapper to trigger functions to disable bike parameters and effectively
* show them as inactive (faded).
*
* @param enable when true parameters are shown
*/
private void showBikeParameters(boolean enable) {
setRangeSeekBarStateColors(enable, mBikeTriangleParameters);
disableEnableControls(enable, mBikeTriangleParametersLayout);
}
/**
* Changes optimization spinner values to show ones compatible with
* bikes or with transit. If updateOptimizationValue is true values of the spinner are also
* modified to adapt to bike.
* <p>
* Replaces fewest transfers with safer trip options.
*
* @param enable when true spinner is set to bike values
* @param updateOptimizationValue when true optimization spinner values are modified to select
* by default "custom trip" by bike and restore previous value
* when bike mode is abandoned
*/
private void setBikeOptimizationAdapter(boolean enable, boolean updateOptimizationValue) {
ArrayAdapter<OptimizeSpinnerItem> optimizationAdapter;
int newOptimizationValue;
if (updateOptimizationValue){
if (enable){
mOptimizationValueToRestoreWhenNoBike = mDdlOptimization.getCheckedItemPosition();
}
}
if (enable) {
optimizationAdapter = new ArrayAdapter<OptimizeSpinnerItem>(
getActivity(),
android.R.layout.simple_list_item_single_choice,
new OptimizeSpinnerItem[]{
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_quick),
OptimizeType.QUICK),
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_safe),
OptimizeType.SAFE),
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_bike_triangle),
OptimizeType.TRIANGLE)});
mDdlOptimization.setAdapter(optimizationAdapter);
} else {
optimizationAdapter = new ArrayAdapter<OptimizeSpinnerItem>(
getActivity(),
android.R.layout.simple_list_item_single_choice,
new OptimizeSpinnerItem[]{
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_quick),
OptimizeType.QUICK),
new OptimizeSpinnerItem(
getResources().getString(R.string.left_panel_optimization_safe),
OptimizeType.SAFE),
new OptimizeSpinnerItem(getResources()
.getString(R.string.left_panel_optimization_fewest_transfers),
OptimizeType.TRANSFERS)});
mDdlOptimization.setAdapter(optimizationAdapter);
}
if (updateOptimizationValue){
if (enable){
newOptimizationValue = 2;
}
else{
if (mOptimizationValueToRestoreWhenNoBike != -1){
newOptimizationValue = mOptimizationValueToRestoreWhenNoBike;
}
else if (mPrefs.getInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, -1) != -1){
newOptimizationValue = mPrefs.getInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, -1);
}
else{
newOptimizationValue = 0;
}
}
mDdlOptimization.setItemChecked(newOptimizationValue, true);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putInt(OTPApp.PREFERENCE_KEY_LAST_OPTIMIZATION, newOptimizationValue);
editor.commit();
}
}
/**
* Toggles between standard colors and faded colors for the passed seekbar
* to visually show that it's disabled.
*
* @param enable when true standard colors are used
* @param seekBar bar that will be toggled
*/
private void setRangeSeekBarStateColors(boolean enable, RangeSeekBar<Double> seekBar) {
if (enable) {
seekBar.setLeftColor(getResources().getColor(R.color.sysRed));
seekBar.setMiddleColor(getResources().getColor(R.color.sysGreen));
seekBar.setRightColor(getResources().getColor(R.color.sysBlue));
} else {
seekBar.setLeftColor(getResources().getColor(R.color.sysRedFaded));
seekBar.setMiddleColor(getResources().getColor(R.color.sysGreenFaded));
seekBar.setRightColor(getResources().getColor(R.color.sysBlueFaded));
}
}
/**
* Recursively enable/disable all the views contained in a ViewGroup and
* it's descendants.
*
* @param enable when true views will be disable
* @param vg a ViewGroup that will be modified
*/
private void disableEnableControls(boolean enable, ViewGroup vg) {
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
if (child instanceof ViewGroup) {
disableEnableControls(enable, (ViewGroup) child);
} else {
if (child != null) {
child.setEnabled(enable);
} else {
Log.w(OTPApp.TAG,
"Not possible to fully perform process to disable all controls");
}
}
}
}
/**
* Removes focus from the text box chosen by the parameter and deletes map click listener if
* none of the text boxes remain focused.
*
* @param isStartTextbox to select text box to removes focus from
*/
private void removeFocus(boolean isStartTextbox) {
if (!mMapFailed){
if (isStartTextbox) {
mTbStartLocation.clearFocus();
if (!mTbEndLocation.hasFocus()){
mMap.setOnMapClickListener(null);
}
} else {
mTbEndLocation.clearFocus();
if (!mTbStartLocation.hasFocus()){
mMap.setOnMapClickListener(null);
}
}
}
}
/**
* Triggers ServerSelector task to retrieve servers list.
* <p>
* Server list will be downloaded or retrieved from the database.
* <p>
* A valid location should be passed to perform server autodetect if the
* preference is set. If location is null a toast will be displayed
* informing of the error.
* <p>
* It it's not possible or not requested to autodetect the server list will
* be displayed.
*
* @param mCurrentLatLng location to use if servers should be detected
*/
public void runAutoDetectServer(LatLng mCurrentLatLng, boolean showDialog) {
if ((mCurrentLatLng == null) || (mMap == null)) {
Toast.makeText(mApplicationContext,
mApplicationContext.getResources().getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
} else {
ServersDataSource dataSource = ServersDataSource.getInstance(mApplicationContext);
WeakReference<Activity> weakContext = new WeakReference<Activity>(getActivity());
ServerSelector serverSelector = new ServerSelector(weakContext, mApplicationContext,
dataSource, this, mNeedToUpdateServersList, showDialog);
serverSelector.execute(mCurrentLatLng);
mSavedLastLocationCheckedForServer = mCurrentLatLng;
}
setNeedToRunAutoDetect(false);
setNeedToUpdateServersList(false);
}
/**
* Triggers ServerSelector task to retrieve servers list.
* <p>
* Server list will be downloaded or retrieved from the database.
* <p>
* A servers list will be displayed or a toast informing of the error.
* <p>
*/
public void runAutoDetectServerNoLocation(boolean showDialog) {
ServersDataSource dataSource = ServersDataSource.getInstance(mApplicationContext);
WeakReference<Activity> weakContext = new WeakReference<Activity>(getActivity());
ServerSelector serverSelector = new ServerSelector(weakContext, mApplicationContext,
dataSource, this, mNeedToUpdateServersList, showDialog);
LatLng latLngList[] = new LatLng[1];
latLngList[0] = null;
serverSelector.execute(latLngList);
setNeedToRunAutoDetect(false);
setNeedToUpdateServersList(false);
}
/**
* Registers the server in the OTPApp class.
* <p>
* UI may be restored to avoid presence of all server data, removing all
* objects from the map and restarting text boxes to default contents.
* <p>
* OTPApp can be requested calling to getActivity by other fragments.
*
* @param s new server to be set
* @param restartUI if true UI will be restarted to adapt to new server
*/
private void setSelectedServer(Server s, boolean restartUI) {
if (restartUI) {
restartMap();
restartTextBoxes();
}
mOTPApp.setSelectedServer(s);
}
/**
* Removes all map objects and the global variables that reference them in
* this fragment.
*/
private void restartMap() {
if (mStartMarker != null) {
mStartMarker.remove();
}
if (mEndMarker != null) {
mEndMarker.remove();
}
if (mModeMarkers != null) {
for (Map.Entry<Marker, TripInfo> entry : mModeMarkers.entrySet()) {
entry.getKey().remove();
}
}
if (mBikeRentalStations != null) {
for (Map.Entry<Marker, BikeRentalStationInfo> entry : mBikeRentalStations.entrySet()) {
entry.getKey().remove();
}
}
if (mRoute != null) {
for (Polyline p : mRoute) {
p.remove();
}
}
if (mBoundariesPolyline != null) {
mBoundariesPolyline.remove();
}
mStartMarker = null;
mStartMarkerPosition = null;
mEndMarker = null;
mEndMarkerPosition = null;
mRoute = null;
mModeMarkers = null;
mBikeRentalStations = null;
mBoundariesPolyline = null;
toggleItinerarySelectionSpinner(false);
}
/**
* Sets text boxes to initial default locations.
* <p>
* MyLocation for start text box and empty for end text box.
* <p>
* Accordingly preference with key PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION
* is set.
*/
private void restartTextBoxes() {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
setTextBoxLocation(mApplicationContext.getResources().getString(R.string.text_box_my_location),
true);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, true);
prefsEditor.commit();
mIsStartLocationGeocodingCompleted = true;
mIsEndLocationGeocodingCompleted = false;
mTbEndLocation.requestFocus();
setTextBoxLocation("", false);
}
/**
* Writes coordinates of latlng to the selected text box.
*
* @param latlng object containing the coordinates to set
* @param isStartTb when true start text box is set otherwise end text box
*/
private void setLocationTb(LatLng latlng, boolean isStartTb) {
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setDecimalSeparator('.');
DecimalFormat decimalFormat = new DecimalFormat(OTPApp.FORMAT_COORDINATES,
decimalFormatSymbols);
if (isStartTb) {
setTextBoxLocation(decimalFormat.format(latlng.latitude) + ", " + decimalFormat
.format(latlng.longitude), true);
} else {
setTextBoxLocation(decimalFormat.format(latlng.latitude) + ", " + decimalFormat
.format(latlng.longitude), false);
}
}
/**
* Moves or adds (if didn't existed) a start/end marker to latlng position
* and updates its text box.
* <p>
* If preference with key PREFERENCE_KEY_USE_INTELLIGENT_MARKERS is set
* geocoding will be triggered for text boxes, except if the parameter geocoding is set to
* false.
* <p>
* If the marker does not fit in selected server bounds marker won't be set
* and a warning will be shown.
*
* @param isStartMarker when true start marker will be set
* @param latlng the location to move on
* @param showMessage whether show or not informative message on success
* @param geocode when false, even with the preference set, geocoding won't be triggered.
*/
private void setMarker(boolean isStartMarker, LatLng latlng, boolean showMessage,
boolean geocode) {
SharedPreferences.Editor prefsEditor = mPrefs.edit();
if (((mOTPApp.getSelectedServer() != null) && LocationUtil
.checkPointInBoundingBox(latlng, mOTPApp.getSelectedServer()))
|| (mOTPApp.getSelectedServer() == null)) {
if (showMessage) {
String toastText;
if (isStartMarker) {
toastText = mApplicationContext.getResources()
.getString(R.string.toast_map_markers_start_marker_activated);
} else {
toastText = mApplicationContext.getResources()
.getString(R.string.toast_map_markers_end_marker_activated);
}
Toast.makeText(mApplicationContext, toastText, Toast.LENGTH_SHORT).show();
}
removeFocus(isStartMarker);
if (isStartMarker) {
if (mStartMarker == null) {
mStartMarker = addStartEndMarker(latlng, true);
} else {
setMarkerPosition(true, latlng);
mStartMarkerPosition = latlng;
}
MainFragment.this.setLocationTb(latlng, true);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, false);
prefsEditor.commit();
if (mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true) && geocode) {
mIsStartLocationGeocodingCompleted = false;
updateMarkerPosition(latlng, true);
} else {
mIsStartLocationGeocodingCompleted = true;
processRequestTrip();
}
} else {
if (mEndMarker == null) {
mEndMarker = addStartEndMarker(latlng, false);
} else {
setMarkerPosition(false, latlng);
mEndMarkerPosition = latlng;
}
MainFragment.this.setLocationTb(latlng, false);
prefsEditor.putBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false);
prefsEditor.commit();
if (mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true) && geocode) {
mIsEndLocationGeocodingCompleted = false;
updateMarkerPosition(latlng, false);
} else {
mIsEndLocationGeocodingCompleted = true;
processRequestTrip();
}
}
} else {
if (showMessage) {
Toast.makeText(mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_map_markers_marker_out_of_boundaries), Toast.LENGTH_SHORT).show();
}
}
}
/**
* Updates marker or creates a new one if doesn't exit to the passed latlng
* <p>
* Accordingly updates the field used for save/restore purposes.
*
* @param isStartMarker if true start marker will be changed, end marker
* otherwise
* @param latLng contains the coordinates of the position to be changed to
*/
private void setMarkerPosition(boolean isStartMarker, LatLng latLng) {
if (isStartMarker) {
if (mStartMarker == null) {
mStartMarker = addStartEndMarker(latLng, true);
} else {
mStartMarker.setPosition(latLng);
}
mStartMarkerPosition = latLng;
} else {
if (mEndMarker == null) {
mEndMarker = addStartEndMarker(latLng, false);
} else {
mEndMarker.setPosition(latLng);
}
mEndMarkerPosition = latLng;
}
}
/**
* Creates and adds to the map a new start/end marker.
* <p>
* Accordingly updates the field used for save/restore purposes.
*
* @param latLng the position to initialize the new marker
* @param isStartMarker if true a start marker will be created
* @return the new marker created
*/
private Marker addStartEndMarker(LatLng latLng, boolean isStartMarker) {
if (!mMapFailed){
MarkerOptions markerOptions = new MarkerOptions().position(latLng)
.draggable(true);
if (isStartMarker) {
markerOptions
.title(mApplicationContext.getResources()
.getString(R.string.map_markers_start_marker_title))
.snippet(mApplicationContext.getResources()
.getString(R.string.map_markers_start_marker_description))
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
mStartMarkerPosition = latLng;
return mMap.addMarker(markerOptions);
} else {
markerOptions
.title(mApplicationContext.getResources().getString(R.string.map_markers_end_marker_title))
.snippet(mApplicationContext.getResources()
.getString(R.string.map_markers_end_marker_description))
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
mEndMarkerPosition = latLng;
return mMap.addMarker(markerOptions);
}
}
return null;
}
private String getLocationTbText(boolean isTbStartLocation) {
if (isTbStartLocation) {
Editable tbStarLocationEditable = mTbStartLocation.getText();
if (tbStarLocationEditable != null) {
return tbStarLocationEditable.toString();
} else {
Log.e(OTPApp.TAG, "Not possible to obtain origin from input box");
}
} else {
Editable tbEndLocationEditable = mTbEndLocation.getText();
if (tbEndLocationEditable != null) {
return tbEndLocationEditable.toString();
} else {
Log.e(OTPApp.TAG, "Not possible to obtain destination from input box");
}
}
return null;
}
/**
* Updates the text box contents to the given location and triggers
* geocoding for that location to update the text box.
* <p>
* This is a wrapper for setLocationTb, processAddress and accordingly change
* the field to control if the text box was changed by the user.
*/
private void updateMarkerPosition(LatLng newLatLng, boolean isStartMarker) {
setLocationTb(newLatLng, isStartMarker);
String locationText = getLocationTbText(isStartMarker);
if (isStartMarker) {
mIsStartLocationChangedByUser = false;
} else {
mIsEndLocationChangedByUser = false;
}
processAddress(isStartMarker, locationText, newLatLng.latitude, newLatLng.longitude, true);
}
@Override
public void onStart() {
super.onStart();
mGoogleApiClient = new GoogleApiClient.Builder(getActivity())
.addApi(API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
if (mMapFailed) {
mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
.getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
enableUIElements(true);
initializeMapInterface(mMap);
runAutoDetectServerNoLocation(true);
}
}
connectLocationClient();
}
/**
* Connects the LocationClient.
* <p>
* To avoid errors only tries if is not pending for another connection
* request or is disconnected.
*/
public void connectLocationClient() {
if (!mGoogleApiClient.isConnected() && !mGoogleApiClient.isConnecting()) {
mGoogleApiClient.connect();
}
}
/**
* Disconnects the LocationClient.
* <p>
* To avoid errors only tries if it's connected.
*/
public void disconnectLocationClient() {
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putBoolean(OTPApp.BUNDLE_KEY_MAP_FAILED, mMapFailed);
if (!mMapFailed) {
bundle.putParcelable(OTPApp.BUNDLE_KEY_MAP_CAMERA, mMap.getCameraPosition());
bundle.putParcelable(OTPApp.BUNDLE_KEY_MAP_START_MARKER_POSITION, mStartMarkerPosition);
bundle.putParcelable(OTPApp.BUNDLE_KEY_MAP_END_MARKER_POSITION, mEndMarkerPosition);
bundle.putBoolean(OTPApp.BUNDLE_KEY_APP_STARTS, mAppStarts);
bundle.putBoolean(OTPApp.BUNDLE_KEY_IS_START_LOCATION_GEOCODING_PROCESSED,
mIsStartLocationGeocodingCompleted);
bundle.putBoolean(OTPApp.BUNDLE_KEY_IS_END_LOCATION_GEOCODING_PROCESSED,
mIsEndLocationGeocodingCompleted);
bundle.putBoolean(OTPApp.BUNDLE_KEY_IS_START_LOCATION_CHANGED_BY_USER,
mIsStartLocationChangedByUser);
bundle.putBoolean(OTPApp.BUNDLE_KEY_IS_END_LOCATION_CHANGED_BY_USER,
mIsEndLocationChangedByUser);
Editable tbStarLocationEditable = mTbStartLocation.getText();
if (tbStarLocationEditable != null) {
bundle.putString(OTPApp.BUNDLE_KEY_TB_START_LOCATION,
tbStarLocationEditable.toString());
} else {
Log.e(OTPApp.TAG, "Not possible to obtain origin while saving app bundle");
}
Editable tbEndLocationEditable = mTbEndLocation.getText();
if (tbEndLocationEditable != null) {
bundle.putString(OTPApp.BUNDLE_KEY_TB_END_LOCATION,
tbEndLocationEditable.toString());
} else {
Log.e(OTPApp.TAG, "Not possible to obtain destination while saving app bundle");
}
bundle.putString(OTPApp.BUNDLE_KEY_TB_END_LOCATION,
mTbEndLocation.getText().toString());
bundle.putParcelable(OTPApp.BUNDLE_KEY_SAVED_LAST_LOCATION, mSavedLastLocation);
bundle.putParcelable(OTPApp.BUNDLE_KEY_SAVED_LAST_LOCATION_CHECKED_FOR_SERVER,
mSavedLastLocationCheckedForServer);
if (mResultTripStartLocation != null) {
bundle.putString(OTPApp.BUNDLE_KEY_RESULT_TRIP_START_LOCATION,
mResultTripStartLocation);
}
if (mResultTripEndLocation != null) {
bundle.putString(OTPApp.BUNDLE_KEY_RESULT_TRIP_END_LOCATION,
mResultTripEndLocation);
}
bundle.putSerializable(OTPApp.BUNDLE_KEY_TRIP_DATE, mTripDate);
bundle.putBoolean(OTPApp.BUNDLE_KEY_ARRIVE_BY, mArriveBy);
bundle.putBoolean(OTPApp.BUNDLE_KEY__IS_ALARM_BIKE_RENTAL_ACTIVE, mIsAlarmBikeRentalUpdateActive);
bundle.putSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_OPTIMIZATION, previousOptimization);
bundle.putSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_MODES, previousModes);
bundle.putSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_BIKE_TRIANGLE_MIN_VALUE, previousBikeTriangleMinValue);
bundle.putSerializable(OTPApp.BUNDLE_KEY_PREVIOUS_BIKE_TRIANGLE_MAX_VALUE, previousBikeTriangleMaxValue);
if (!mFragmentListener.getCurrentItineraryList().isEmpty()) {
OTPBundle otpBundle = new OTPBundle();
otpBundle.setFromText(mResultTripStartLocation);
otpBundle.setToText(mResultTripEndLocation);
otpBundle.setItineraryList(mFragmentListener.getCurrentItineraryList());
otpBundle.setCurrentItineraryIndex(mFragmentListener.getCurrentItineraryIndex());
otpBundle.setCurrentItinerary(mFragmentListener.getCurrentItinerary());
bundle.putSerializable(OTPApp.BUNDLE_KEY_OTP_BUNDLE, otpBundle);
}
bundle.putSerializable(OTPApp.BUNDLE_KEY_CUSTOM_SERVER_METADATA, mCustomServerMetadata);
}
}
/**
* Triggers geocoding for chosen text box with passed text.
* <p>
* If address contents are the String used to identify user's location
* ("MyLocation" for example) user location is passed to know the
* corresponding address.
* In this case user's location shouldn't be null, if it is a toast is
* shown.
*/
public void processAddress(final boolean isStartTextBox, String address,
boolean geocodingForMarker) {
processAddress(isStartTextBox, address, null, null, geocodingForMarker);
}
/**
* Triggers geocoding for chosen text box with passed text, offering the possibility of pass
* original latitude and longitude requested to check reverse geocoding results when
* geocoding for marker.
* <p>
* If address contents are the String used to identify user's location
* ("MyLocation" for example) user location is passed to know the
* corresponding address.
* In this case user's location shouldn't be null, if it is a toast is
* shown.
*/
public void processAddress(final boolean isStartTextBox, String address, Double originalLat,
Double originalLon, boolean geocodingForMarker) {
WeakReference<Activity> weakContext = new WeakReference<Activity>(getActivity());
mGeoCodingTask = new OTPGeocoding(weakContext, mApplicationContext,
isStartTextBox, geocodingForMarker, mOTPApp.getSelectedServer(), this);
LatLng mCurrentLatLng = getLastLocation();
if (address.equalsIgnoreCase(this.getResources().getString(R.string.text_box_my_location))) {
if (mCurrentLatLng != null) {
if (isStartTextBox){
mIsStartLocationGeocodingCompleted = false;
mGeoCodingTask.execute(address, String.valueOf(mCurrentLatLng.latitude),
String.valueOf(mCurrentLatLng.longitude));
}
else{
mIsEndLocationGeocodingCompleted = false;
mGeoCodingTask.execute(address, String.valueOf(mCurrentLatLng.latitude),
String.valueOf(mCurrentLatLng.longitude));
}
} else {
Toast.makeText(mApplicationContext,
mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error),
Toast.LENGTH_LONG).show();
}
} else {
String latString, lonString;
if (originalLat != null && originalLon != null){
latString = originalLat.toString();
lonString = originalLon.toString();
}
else {
latString = null;
lonString = null;
}
if (isStartTextBox){
mIsStartLocationGeocodingCompleted = false;
mGeoCodingTask.execute(address, latString, lonString);
}
else{
mIsEndLocationGeocodingCompleted = false;
mGeoCodingTask.execute(address, latString, lonString);
}
}
}
@Override
public void onResume() {
super.onResume();
listenForBikeUpdates(mIsAlarmBikeRentalUpdateActive);
Log.d(OTPApp.TAG, "MainFragment onResume");
}
@Override
public void onPause() {
super.onPause();
listenForBikeUpdates(false);
}
@Override
public void onStop() {
disconnectLocationClient();
super.onStop();
}
@Override
public void onDestroy() {
// Release all map-related objects to make sure GPS is shut down when
// the user leaves the app
Log.d(OTPApp.TAG, "Released all map objects in MainFragment.onDestroy()");
super.onDestroy();
}
/**
* Updates server to the new one set in preferences and also makes some UI changes (camera
* movements) if specified.
*
* @param updateUI also updateUI, not useful if changes should occur on background
*/
public void updateSelectedServer(boolean updateUI) {
long serverId;
Server server;
if (!mMapFailed){
if (mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_SELECTED_CUSTOM_SERVER, false)) {
server = new Server(mPrefs.getString(OTPApp.PREFERENCE_KEY_CUSTOM_SERVER_URL, ""),
mApplicationContext);
setSelectedServer(server, updateUI);
String bounds;
if ((bounds = mPrefs.getString(OTPApp.PREFERENCE_KEY_CUSTOM_SERVER_BOUNDS, null))
!= null) {
server.setBounds(bounds);
addBoundariesRectangle(server);
}
WeakReference<Activity> weakContext = new WeakReference<Activity>(getActivity());
if (mCustomServerMetadata == null){
MetadataRequest metaRequest = new MetadataRequest(weakContext, mApplicationContext,
this);
metaRequest.execute(mPrefs.getString(OTPApp.PREFERENCE_KEY_CUSTOM_SERVER_URL, ""));
}
else{
onMetadataRequestComplete(mCustomServerMetadata, false);
}
Log.d(OTPApp.TAG, "Now using custom OTP server: " + mPrefs
.getString(OTPApp.PREFERENCE_KEY_CUSTOM_SERVER_URL, ""));
} else if ((serverId = mPrefs.getLong(OTPApp.PREFERENCE_KEY_SELECTED_SERVER, 0)) != 0){
mCustomServerMetadata = null;
ServersDataSource dataSource = ServersDataSource.getInstance(mApplicationContext);
dataSource.open();
server = new Server(dataSource
.getServer(mPrefs.getLong(OTPApp.PREFERENCE_KEY_SELECTED_SERVER, 0)));
dataSource.close();
setSelectedServer(server, updateUI);
addBoundariesRectangle(server);
if (updateUI){
LatLng mCurrentLatLng = getLastLocation();
if ((mCurrentLatLng != null) && (LocationUtil
.checkPointInBoundingBox(mCurrentLatLng, server))) {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(mCurrentLatLng, getServerInitialZoom(server)));
} else {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(getServerCenter(server), getServerInitialZoom(server)));
setMarker(true, getServerCenter(server), false, true);
}
}
Log.d(OTPApp.TAG, "Now using OTP server: " + server.getRegion());
} else {
Log.d(OTPApp.TAG, "Server not selected yet, should be first start or app update");
return;
}
if (server != null){
if (server.getOffersBikeRental()){
mBtnModeRentedBike.setEnabled(true);
}
else{
mBtnModeRentedBike.setChecked(false);
mBtnModeRentedBike.setEnabled(false);
}
if (server.getOffersBikeRental() && mBtnModeRentedBike.isChecked()){
BikeRentalLoad bikeRentalGetStations = new BikeRentalLoad(mApplicationContext, true,
this);
bikeRentalGetStations.execute(server.getBaseURL());
}
else{
removeBikeStations();
listenForBikeUpdates(false);
}
if (startLocationPlacesAutoCompleteAdapter != null
&& endLocationPlacesAutoCompleteAdapter != null){
startLocationPlacesAutoCompleteAdapter.setSelectedServer(server);
endLocationPlacesAutoCompleteAdapter.setSelectedServer(server);
}
}
}
}
@Override
public void onCreateOptionsMenu(Menu pMenu, MenuInflater inflater) {
super.onCreateOptionsMenu(pMenu, inflater);
inflater.inflate(R.menu.menu, pMenu);
mGPS = pMenu.getItem(0);
}
@Override
public void onPrepareOptionsMenu(final Menu pMenu) {
if (isGPSEnabled()) {
mGPS.setTitle(R.string.menu_button_disable_gps);
} else {
mGPS.setTitle(R.string.menu_button_enable_gps);
}
super.onPrepareOptionsMenu(pMenu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem pItem) {
OTPApp app = ((OTPApp) getActivity().getApplication());
switch (pItem.getItemId()) {
case R.id.gps_settings:
Intent myIntent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(myIntent);
break;
case R.id.settings:
getActivity().startActivityForResult(
new Intent(getActivity(), SettingsActivity.class),
OTPApp.SETTINGS_REQUEST_CODE);
break;
case R.id.feedback:
Server selectedServer = app.getSelectedServer();
String[] recipients = {selectedServer.getContactEmail(),
getString(R.string.feedback_email_android_developer)};
String uriText = "mailto:";
for (String recipient : recipients) {
uriText += recipient + ";";
}
String subject = "";
subject += getResources().getString(R.string.menu_button_feedback_subject);
Date d = Calendar.getInstance().getTime();
subject += "[" + d.toString() + "]";
uriText += "?subject=" + subject;
String content = ((MyActivity) getActivity()).getCurrentRequestString();
try {
uriText += "&body=" + URLEncoder.encode(content, OTPApp.URL_ENCODING);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
return false;
}
Uri uri = Uri.parse(uriText);
Intent sendIntent = new Intent(Intent.ACTION_SENDTO);
sendIntent.setData(uri);
startActivity(Intent.createChooser(sendIntent,
getResources().getString(R.string.menu_button_feedback_send_email)));
break;
case R.id.server_info:
Server server = app.getSelectedServer();
if (server == null) {
Log.w(OTPApp.TAG,
"Tried to get server info when no server was selected");
Toast.makeText(mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_no_server_selected_error), Toast.LENGTH_SHORT)
.show();
break;
}
WeakReference<Activity> weakContext = new WeakReference<Activity>(getActivity());
ServerChecker serverChecker = new ServerChecker(weakContext, mApplicationContext,
true);
serverChecker.execute(server);
break;
default:
break;
}
return false;
}
private Boolean isGPSEnabled() {
return sLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
/**
* Wrapper to other functions: moves the marker to the location included
* in the address, updates text box and zooms to that position.
*
* @param isStartMarker if true start marker will be changed
* @param address will location and text information
*/
public void moveMarker(Boolean isStartMarker, CustomAddress address) {
if (isStartMarker) {
mStartAddress = address;
} else {
mEndAddress = address;
}
LatLng latlng = new LatLng(address.getLatitude(), address.getLongitude());
setMarkerPosition(isStartMarker, latlng);
setTextBoxLocation(address.toString(), isStartMarker);
zoomToGeocodingResult(isStartMarker, address);
}
/**
* Wrapper to other functions: moves the marker to the location included
* in the address, updates text box and zooms to that position.
* <p>
* This only happens if the new location is closer than a constant to
* marker previous location. Otherwise address is only used as reference
* and text box is updated to "Marker close to [address]".
*
* @param isStartMarker if true start marker will be changed
* @param address will location and text information
*/
public void moveMarkerRelative(Boolean isStartMarker, CustomAddress address) {
float results[] = new float[1];
double addressLat = address.getLatitude();
double addressLon = address.getLongitude();
Marker marker;
if (isStartMarker) {
marker = mStartMarker;
mStartAddress = address;
} else {
marker = mEndMarker;
mEndAddress = address;
}
Location.distanceBetween(marker.getPosition().latitude, marker.getPosition().longitude,
addressLat, addressLon, results);
if (results[0] < OTPApp.GEOCODING_MAX_ERROR) {
LatLng newLatlng = new LatLng(addressLat, addressLon);
setMarkerPosition(isStartMarker, newLatlng);
setTextBoxLocation(address.toString(), isStartMarker);
} else {
setTextBoxLocation(getResources().getString(R.string.text_box_close_to_marker) + " "
+ address.toString(), isStartMarker);
}
}
/**
* Zooms to address or to address and the location of the other marker if it's
* not the first marker.
* <p>
* If the other location is "MyLocation" will also be included in zoom.
*
* @param isStartLocation if true address is for start location
* @param address with the location to zoom at
*/
public void zoomToGeocodingResult(boolean isStartLocation, CustomAddress address) {
LatLng latlng = new LatLng(address.getLatitude(), address.getLongitude());
LatLng mCurrentLatLng = getLastLocation();
if (!mMapFailed){
if (isStartLocation) {
if (mIsStartLocationChangedByUser) {
if (mEndMarker != null) {
zoomToTwoPoints(latlng, mEndMarkerPosition);
} else if (mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_DESTINATION_IS_MY_LOCATION, false)) {
if (mCurrentLatLng == null) {
Toast.makeText(mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error), Toast.LENGTH_LONG)
.show();
} else {
zoomToTwoPoints(latlng, mCurrentLatLng);
}
} else {
zoomToLocation(latlng);
}
}
} else {
if (mIsEndLocationChangedByUser) {
if (mStartMarker != null) {
zoomToTwoPoints(mStartMarkerPosition, latlng);
} else if (mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_ORIGIN_IS_MY_LOCATION, false)) {
if (mCurrentLatLng == null) {
Toast.makeText(mApplicationContext, mApplicationContext.getResources()
.getString(R.string.toast_tripplanner_current_location_error), Toast.LENGTH_LONG)
.show();
} else {
zoomToTwoPoints(mCurrentLatLng, latlng);
}
} else {
zoomToLocation(latlng);
}
}
}
}
}
public void zoomToLocation(LatLng latlng) {
if (latlng != null) {
mMap.animateCamera(
CameraUpdateFactory.newLatLngZoom(latlng, OTPApp.defaultMediumZoomLevel));
}
}
public void zoomToTwoPoints(LatLng pointA, LatLng pointB) {
if ((pointA.latitude != pointB.latitude) && (pointA.longitude != pointB.longitude)) {
LatLngBounds.Builder boundsCreator = LatLngBounds.builder();
boundsCreator.include(pointA);
boundsCreator.include(pointB);
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(boundsCreator.build(),
getResources().getInteger(R.integer.route_zoom_padding)));
}
}
/**
* Updates start/end text box contents to the given text.
*
* @param text contents to insert
* @param isStartTextBox if true start box will be used
*/
public void setTextBoxLocation(String text, boolean isStartTextBox) {
if (isStartTextBox) {
mIsStartLocationChangedByUser = false;
mTbStartLocation.setText(text);
} else {
mIsEndLocationChangedByUser = false;
mTbEndLocation.setText(text);
}
}
/**
* Resets start/end text box to previous valid address.
*
* @param isStartTextBox if true start box will be used
*/
private void restartTextBoxLocation(boolean isStartTextBox) {
if (isStartTextBox) {
if (mStartAddress != null) {
mIsStartLocationChangedByUser = false;
mTbStartLocation.setText(addressToString(mStartAddress));
}
} else {
if (mEndAddress != null) {
mIsEndLocationChangedByUser = false;
mTbEndLocation.setText(addressToString(mEndAddress));
}
}
}
/**
* Returns address in string format.
* <p>
* Lines used are first and second.
*
* @param add the address to transform
*/
private String addressToString(CustomAddress add) {
return ((add.getAddressLine(0) != null) ? add.getAddressLine(0) : "")
+ ", "
+ ((add.getAddressLine(1) != null) ? add.getAddressLine(1) : "");
}
/**
* Draws the route on the map.
* <p>
* To indicate the full route a polyline will be drawn using all points in
* itinerary.
* <p>
* On each method of transportation change a mode marker will be added.
* <p>
* Mode marker for transit step will display stop name, departure time and
* headsign.
* Mode marker for walk/bike connection, guidance to next point and distance and time
* to get there.
* <p>
* Previous routes are removed from the map.
*
* @param itinerary the information to be drawn
* @param animateCamera type of camera animation: - 0 camera wouldn't be animated
* - 1 animated to fit the route
* - 2 animated to fit first transit marker if
* any, otherwise to route.
*/
public void showRouteOnMap(List<Leg> itinerary, int animateCamera) {
Log.d(OTPApp.TAG,
"(TripRequest) legs size = "
+ Integer.toString(itinerary.size()));
if (mRoute != null) {
for (Polyline legLine : mRoute) {
legLine.remove();
}
mRoute.clear();
}
if (mModeMarkers != null) {
for (Map.Entry<Marker, TripInfo> entry : mModeMarkers.entrySet()) {
entry.getKey().remove();
}
}
mRoute = new ArrayList<Polyline>();
mModeMarkers = new HashMap<Marker, TripInfo>();
Marker firstTransitMarker = null;
if (!itinerary.isEmpty() && !mMapFailed) {
LatLngBounds.Builder boundsCreator = LatLngBounds.builder();
int stepIndex = 0;
for (Leg leg : itinerary) {
stepIndex++;
List<LatLng> points = LocationUtil.decodePoly(leg.legGeometry
.getPoints());
if (!points.isEmpty()) {
MarkerOptions modeMarkerOption = generateModeMarkerOptions(leg, points.get(0),
stepIndex);
float scaleFactor = getResources().getFraction(R.fraction.scaleFactor, 1, 1);
Marker modeMarker = mMap.addMarker(modeMarkerOption);
boolean realtime = false;
if (TraverseMode.valueOf(leg.mode).isTransit()) {
realtime = leg.realTime;
}
TripInfo tripInfo = new TripInfo(realtime, leg.tripId,
generateModeMarkerSnippet(leg), leg.departureDelay);
mModeMarkers.put(modeMarker, tripInfo);
if (TraverseMode.valueOf(leg.mode).isTransit()) {
//because on transit two step-by-step indications are generated (get on / get off)
stepIndex++;
if (firstTransitMarker == null) {
firstTransitMarker = modeMarker;
}
}
PolylineOptions options = new PolylineOptions().addAll(points)
.width(5 * scaleFactor)
.color(OTPApp.COLOR_ROUTE_LINE);
Polyline routeLine = mMap.addPolyline(options);
mRoute.add(routeLine);
for (LatLng point : points) {
boundsCreator.include(point);
}
}
}
mCustomInfoWindowAdapter.setMarkers(mModeMarkers);
mMap.setInfoWindowAdapter(mCustomInfoWindowAdapter);
if (animateCamera == 1){
if (firstTransitMarker != null){
firstTransitMarker.showInfoWindow();
}
}
if (animateCamera > 0) {
LatLngBounds routeBounds = boundsCreator.build();
if (((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map))
.getMap()
!= null){
showRouteOnMapAnimateCamera(routeBounds, firstTransitMarker, animateCamera);
}
}
}
}
private MarkerOptions generateModeMarkerOptions(Leg leg, LatLng location, int stepIndex){
MarkerOptions modeMarkerOption = new MarkerOptions().position(location);
Drawable drawable = getResources().getDrawable(getPathIcon(leg.mode));
if (drawable != null) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable.getCurrent();
Bitmap bitmap = bitmapDrawable.getBitmap();
modeMarkerOption.icon(
BitmapDescriptorFactory.fromBitmap(bitmap));
} else {
Log.e(OTPApp.TAG, "Error obtaining drawable to add mode icons to the map");
}
modeMarkerOption.title(generateModeMarkerTitle(leg, stepIndex));
return modeMarkerOption;
}
private String generateModeMarkerTitle(Leg leg, int stepIndex){
TraverseMode traverseMode = TraverseMode.valueOf(leg.mode);
String title = "";
if (traverseMode.isTransit()) {
title = stepIndex + ". " + ConversionUtils
.getRouteShortNameSafe(leg.routeShortName, leg.routeLongName,
mApplicationContext)
+ " " + getResources().getString(R.string.map_markers_connector_before_stop) + " "
+ DirectionsGenerator.getLocalizedStreetName(leg.from.name,
mApplicationContext.getResources());
}
else{
if (traverseMode.equals(TraverseMode.WALK)) {
title = stepIndex + ". " + getResources()
.getString(R.string.map_markers_mode_walk_action)
+ " " + getResources().getString(R.string.map_markers_connector_before_destination)
+ " " + DirectionsGenerator.getLocalizedStreetName(leg.to.name,
mApplicationContext.getResources());
} else if (traverseMode.equals(TraverseMode.BICYCLE)) {
title = stepIndex + ". " + getResources()
.getString(R.string.map_markers_mode_bicycle_action)
+ " " + getResources().getString(R.string.map_markers_connector_before_destination)
+ " " + DirectionsGenerator.getLocalizedStreetName(leg.to.name,
mApplicationContext.getResources());
}
}
return title;
}
private CharSequence generateModeMarkerSnippet(Leg leg) {
CharSequence snippet;
long legDuration;
TraverseMode traverseMode = TraverseMode.valueOf(leg.mode);
legDuration = ConversionUtils.normalizeDuration(leg.duration, mPrefs);
if (traverseMode.isTransit()) {
CharSequence spannableSnippet = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.startTime), false);
if (leg.realTime){
int color = ConversionUtils.getDelayColor(leg.departureDelay, mApplicationContext);
spannableSnippet = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.startTime), false, color);
}
if (leg.headsign != null) {
snippet = TextUtils.concat(spannableSnippet, " ",
getResources().getString(R.string.step_by_step_non_transit_to),
" ", leg.headsign);
}
else{
snippet = spannableSnippet;
}
} else {
snippet = ConversionUtils
.getFormattedDurationTextNoSeconds(legDuration, false,
mApplicationContext) + " " + "-" + " "
+ ConversionUtils
.getFormattedDistance(leg.distance, mApplicationContext);
}
return snippet;
}
/**
* Moves the camera to correctly display the route on map. Several options are possible to move
* the camera according to animateCamera parameter.
* <p>
* If the route contains any transit leg, Info Window of start point of the first one will be
* opened and centered in the screen, to display stop address from where route starts.
* To fit the whole route in the screen and let the first transit stop in the center, next steps
* are performed (the objective is to create new bounds with this characteristic):
* - Measure screen horizontal and vertical distance to northeast and southwest points of route
* bounds. Obtain the highest.
* - Add this distance in the other 3 dimensions to the first transit marker screen position in
* order to obtain new points to have all the route fitted and the transit marker in the middle
* (approximately due to projection restrictions).
* - Calculate the coordinates of these new points and add them to the route points to calculate
* the new bounds.
* - Pass the new bounds to the camera movement function and correct result will be obtained.
*
* @param routeBounds original route bounds
* @param firstTransitMarker position of first transit stop that will be centered
* @param animateCamera type of camera animation: - 0 camera wouldn't be animated
* - 1 animated to fit the route
* - 2 animated to fit first transit marker if
* any, otherwise to route.
* - 3 moved with no animation to fit first
* transit marker if any, otherwise to route.
*/
private void showRouteOnMapAnimateCamera(LatLngBounds routeBounds, Marker firstTransitMarker, int animateCamera){
int windowWidth = getResources().getDisplayMetrics().widthPixels;
int windowHeight = getResources().getDisplayMetrics().heightPixels;
int limitNortheastRight = windowWidth - mMapPaddingRight;
int limitNortheastTop = mMapPaddingTop;
int limitSouthwestLeft = mMapPaddingLeft;
int limitSouthwestBottom = windowHeight - mMapPaddingBottom;
Point northeastInScreen = mMap.getProjection().toScreenLocation(routeBounds.northeast);
Point southwestInScreen = mMap.getProjection().toScreenLocation(routeBounds.southwest);
if ((firstTransitMarker != null) && (animateCamera == 1)){
Point firstTransitMarkerInScreen = mMap.getProjection().toScreenLocation(firstTransitMarker.getPosition());
int maxDistanceToRouteEdge = 0;
int distanceHorizontalNortheast = northeastInScreen.x - firstTransitMarkerInScreen.x;
int distanceVerticalNortheast = firstTransitMarkerInScreen.y - northeastInScreen.y;
int distanceHorizontalSouthwest = firstTransitMarkerInScreen.x - southwestInScreen.x;
int distanceVerticalSouthwest = southwestInScreen.y - firstTransitMarkerInScreen.y;
if (distanceHorizontalNortheast > maxDistanceToRouteEdge){
maxDistanceToRouteEdge = distanceHorizontalNortheast;
}
if (distanceVerticalNortheast > maxDistanceToRouteEdge){
maxDistanceToRouteEdge = distanceVerticalNortheast;
}
if (distanceHorizontalSouthwest > maxDistanceToRouteEdge){
maxDistanceToRouteEdge = distanceHorizontalSouthwest;
}
if (distanceVerticalSouthwest > maxDistanceToRouteEdge) {
maxDistanceToRouteEdge = distanceVerticalSouthwest;
}
maxDistanceToRouteEdge = Math.abs(maxDistanceToRouteEdge);
Point newLimitSouthWest = new Point(firstTransitMarkerInScreen.x - maxDistanceToRouteEdge, firstTransitMarkerInScreen.y + maxDistanceToRouteEdge);
Point newLimitNorthEast = new Point(firstTransitMarkerInScreen.x + maxDistanceToRouteEdge, firstTransitMarkerInScreen.y - maxDistanceToRouteEdge);
LatLng newLimitSouthWestLatLng = mMap.getProjection().fromScreenLocation(newLimitSouthWest);
LatLng newLimitNorthEastLatLng = mMap.getProjection().fromScreenLocation(newLimitNorthEast);
if (newLimitSouthWestLatLng != null && newLimitNorthEastLatLng != null){
if (newLimitNorthEastLatLng.latitude < newLimitSouthWestLatLng.latitude){
newLimitNorthEastLatLng = new LatLng(newLimitSouthWestLatLng.latitude,
newLimitNorthEastLatLng.longitude);
newLimitSouthWestLatLng = new LatLng(newLimitNorthEastLatLng.latitude,
newLimitSouthWestLatLng.longitude);
}
if (newLimitNorthEastLatLng.longitude < newLimitSouthWestLatLng.longitude){
newLimitNorthEastLatLng = new LatLng(newLimitNorthEastLatLng.latitude,
newLimitSouthWestLatLng.longitude);
newLimitSouthWestLatLng = new LatLng(newLimitSouthWestLatLng.latitude,
newLimitNorthEastLatLng.longitude);
}
routeBounds = new LatLngBounds(newLimitSouthWestLatLng, newLimitNorthEastLatLng);
}
}
int routeDefaultPadding = getResources().getInteger(R.integer.route_zoom_padding);
routeDefaultPadding = Math.round(routeDefaultPadding * getResources().getDisplayMetrics().density);
int padding = routeDefaultPadding;
int maxHorizontalPadding = (limitNortheastRight - limitSouthwestLeft) / 2;
int maxVerticalPadding = (limitSouthwestBottom - limitNortheastTop) / 2;
if (padding > maxHorizontalPadding){
padding = maxHorizontalPadding;
}
if (padding > maxVerticalPadding){
padding = maxVerticalPadding;
}
if (animateCamera == 3){
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(routeBounds, padding));
}
else{
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(routeBounds, padding));
}
}
private int getPathIcon(String modeString) {
TraverseMode mode = TraverseMode.valueOf(modeString);
int icon;
if (mode.compareTo(TraverseMode.BICYCLE) == 0) {
icon = R.drawable.cycling;
} else if (mode.compareTo(TraverseMode.CAR) == 0) {
icon = R.drawable.car;
} else if ((mode.compareTo(TraverseMode.BUS) == 0) || (mode.compareTo(TraverseMode.BUSISH)
== 0)) {
icon = R.drawable.bus;
} else if ((mode.compareTo(TraverseMode.RAIL) == 0) || (
mode.compareTo(TraverseMode.TRAINISH) == 0)) {
icon = R.drawable.train;
} else if (mode.compareTo(TraverseMode.FERRY) == 0) {
icon = R.drawable.ferry;
} else if (mode.compareTo(TraverseMode.GONDOLA) == 0) {
icon = R.drawable.boat;
} else if (mode.compareTo(TraverseMode.SUBWAY) == 0) {
icon = R.drawable.underground;
} else if (mode.compareTo(TraverseMode.TRAM) == 0) {
icon = R.drawable.tramway;
} else if (mode.compareTo(TraverseMode.WALK) == 0) {
icon = R.drawable.pedestriancrossing;
} else if (mode.compareTo(TraverseMode.CABLE_CAR) == 0) {
icon = R.drawable.cablecar;
} else if (mode.compareTo(TraverseMode.FUNICULAR) == 0) {
icon = R.drawable.funicolar;
} else if (mode.compareTo(TraverseMode.TRANSIT) == 0) {
icon = R.drawable.road;
} else if (mode.compareTo(TraverseMode.TRANSFER) == 0) {
icon = R.drawable.caution;
} else {
icon = R.drawable.road;
}
return icon;
}
public OtpFragment getFragmentListener() {
return mFragmentListener;
}
public void setFragmentListener(OtpFragment fragmentListener) {
this.mFragmentListener = fragmentListener;
}
@Override
public void onServerSelectorComplete(Server server) {
//Update application server
if (getActivity() != null) {
updateSelectedServer(true);
}
}
@Override
public void onTripRequestComplete(List<Itinerary> itineraries,
String currentRequestString) {
if (getActivity() != null) {
ConversionUtils.fixTimezoneOffsets(itineraries,
mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_USE_DEVICE_TIMEZONE, false));
fillItinerariesSpinner(itineraries);
toggleItinerarySelectionSpinner(!itineraries.isEmpty());
OtpFragment ofl = getFragmentListener();
// onItinerariesLoaded must be invoked before onItinerarySelected(0)
ofl.onItinerariesLoaded(itineraries);
ofl.onItinerarySelected(0, 1);
MyActivity myActivity = (MyActivity) getActivity();
myActivity.setCurrentRequestString(currentRequestString);
if ((mStartAddress != null) && (mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true))) {
mResultTripStartLocation = addressToString(mStartAddress);
} else {
Editable tbStarLocationEditable = mTbStartLocation.getText();
if (tbStarLocationEditable != null) {
mResultTripStartLocation = tbStarLocationEditable.toString();
} else {
Log.e(OTPApp.TAG,
"Not possible to obtain origin from input box while saving it to"
+ " step-by-step screen");
}
}
if ((mEndAddress != null) && (mPrefs
.getBoolean(OTPApp.PREFERENCE_KEY_USE_INTELLIGENT_MARKERS, true))) {
mResultTripEndLocation = addressToString(mEndAddress);
} else {
Editable tbEndLocationEditable = mTbEndLocation.getText();
if (tbEndLocationEditable != null) {
mResultTripEndLocation = tbEndLocationEditable.toString();
} else {
Log.e(OTPApp.TAG,
"Not possible to obtain destination from input box while saving it to"
+ " step-by-step screen");
}
}
removeFocus(true);
removeFocus(false);
}
}
private void fillItinerariesSpinner(List<Itinerary> itineraryList) {
String[] itinerarySummaryList = new String[itineraryList.size()];
long tripDuration;
for (int i = 0; i < itinerarySummaryList.length; i++) {
boolean isTransitIsTagSet = false;
Itinerary it = itineraryList.get(i);
tripDuration = ConversionUtils.normalizeDuration(it.duration, mPrefs);
for (Leg leg : it.legs) {
TraverseMode traverseMode = TraverseMode.valueOf(leg.mode);
if (traverseMode.isTransit()) {
itinerarySummaryList[i] = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.startTime), false).toString();
itinerarySummaryList[i] += ". " + ConversionUtils
.getRouteShortNameSafe(leg.routeShortName,leg.routeLongName,
mApplicationContext);
itinerarySummaryList[i] += " - " + ConversionUtils
.getFormattedDurationTextNoSeconds(tripDuration, false,
mApplicationContext);
if (leg.headsign!= null) {
itinerarySummaryList[i] += " - " + leg.headsign;
}
isTransitIsTagSet = true;
break;
}
}
if (!isTransitIsTagSet) {
itinerarySummaryList[i] = Integer.toString(i + 1)
+ ". ";//Shown index is i + 1, to use 1-based indexes for the UI instead of 0-based
itinerarySummaryList[i] +=
ConversionUtils.getFormattedDistance(it.walkDistance, mApplicationContext)
+ " " + "-" + " " + ConversionUtils
.getFormattedDurationTextNoSeconds(tripDuration, false,
mApplicationContext);
}
}
ArrayAdapter<String> itineraryAdapter = new ArrayAdapter<String>(this.getActivity(),
android.R.layout.simple_spinner_item, itinerarySummaryList);
itineraryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mItinerarySelectionSpinner.setAdapter(itineraryAdapter);
}
@Override
public void onOTPGeocodingComplete(final boolean isStartTextbox,
ArrayList<CustomAddress> addressesReturn, boolean geocodingForMarker) {
if (getActivity() != null) {
try {
AlertDialog.Builder geocoderAlert = new AlertDialog.Builder(
getActivity());
geocoderAlert.setTitle(R.string.geocoder_results_title)
.setMessage(R.string.geocoder_results_no_results_message)
.setCancelable(false)
.setPositiveButton(getResources().getString(android.R.string.ok),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
if (addressesReturn.isEmpty()) {
restartTextBoxLocation(isStartTextbox);
AlertDialog alert = geocoderAlert.create();
alert.show();
return;
} else if (addressesReturn.size() == 1) {
useNewAddress(isStartTextbox, addressesReturn.get(0), geocodingForMarker);
return;
}
AlertDialog.Builder geocoderSelector = new AlertDialog.Builder(
getActivity());
geocoderSelector.setTitle(R.string.geocoder_results_title);
final CharSequence[] addressesText = new CharSequence[addressesReturn
.size()];
for (int i = 0; i < addressesReturn.size(); i++) {
CustomAddress address = addressesReturn.get(i);
addressesText[i] = address.getStringAddress(true);
Log.d(OTPApp.TAG, addressesText[i].toString());
}
final ArrayList<CustomAddress> addressesTemp = addressesReturn;
geocoderSelector.setItems(addressesText,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
CustomAddress address = addressesTemp.get(item);
Log.d(OTPApp.TAG, "Chosen: " + addressesText[item]);
useNewAddress(isStartTextbox, address, false);
}
});
AlertDialog alertGeocoder = geocoderSelector.create();
alertGeocoder.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
restartTextBoxLocation(isStartTextbox);
}
});
alertGeocoder.show();
} catch (Exception e) {
Log.e(OTPApp.TAG, "Error in Main Fragment Geocoding callback: " + e);
}
}
}
public void useNewAddress(final boolean isStartTextbox, CustomAddress newAddress,
boolean geocodingForMarker) {
removeFocus(isStartTextbox);
if (isStartTextbox) {
mIsStartLocationGeocodingCompleted = true;
} else {
mIsEndLocationGeocodingCompleted = true;
}
if (geocodingForMarker) {
moveMarkerRelative(isStartTextbox, newAddress);
} else {
moveMarker(isStartTextbox, newAddress);
}
processRequestTrip();
changingTextBoxWithAutocomplete = false;
}
@Override
public void onMetadataRequestComplete(GraphMetadata metadata, boolean updateUI) {
if (getActivity() != null && !mMapFailed) {
if (metadata != null){
mCustomServerMetadata = metadata;
double lowerLeftLatitude = metadata.getLowerLeftLatitude();
double lowerLeftLongitude = metadata.getLowerLeftLongitude();
double upperRightLatitude = metadata.getUpperRightLatitude();
double upperRightLongitude = metadata.getUpperRightLongitude();
Server selectedServer = mOTPApp.getSelectedServer();
String bounds = String.valueOf(lowerLeftLatitude) +
"," + String.valueOf(lowerLeftLongitude) +
"," + String.valueOf(upperRightLatitude) + "," + String
.valueOf(upperRightLongitude);
selectedServer.setBounds(bounds);
SharedPreferences.Editor prefsEditor = PreferenceManager
.getDefaultSharedPreferences(mApplicationContext).edit();
prefsEditor.putString(OTPApp.PREFERENCE_KEY_CUSTOM_SERVER_BOUNDS, bounds);
prefsEditor.commit();
Log.d(OTPApp.TAG, "LowerLeft: " + Double.toString(lowerLeftLatitude) + "," + Double
.toString(lowerLeftLongitude));
Log.d(OTPApp.TAG, "UpperRight" + Double.toString(upperRightLatitude) + "," + Double
.toString(upperRightLongitude));
addBoundariesRectangle(selectedServer);
LatLng mCurrentLatLng = getLastLocation();
if (updateUI){
if ((mCurrentLatLng != null) && (LocationUtil
.checkPointInBoundingBox(mCurrentLatLng, selectedServer))) {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(mCurrentLatLng, getServerInitialZoom(selectedServer)));
} else {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(getServerCenter(selectedServer),
getServerInitialZoom(selectedServer)));
setMarker(true, getServerCenter(selectedServer), false, true);
}
}
}
}
}
/**
* Changes the tiles used to display the map and sets max zoom level.
*
* @param overlayString tiles URL for custom tiles or description for
* Google ones
*/
public void updateOverlay(String overlayString) {
if (!mMapFailed){
int tile_width = OTPApp.CUSTOM_MAP_TILE_SMALL_WIDTH;
int tile_height = OTPApp.CUSTOM_MAP_TILE_SMALL_HEIGHT;
if (overlayString == null) {
overlayString = ConversionUtils.getOverlayString(mApplicationContext);
}
if (mSelectedTileOverlay != null) {
mSelectedTileOverlay.remove();
}
if (overlayString.startsWith(OTPApp.MAP_TILE_GOOGLE)) {
int mapType = GoogleMap.MAP_TYPE_NORMAL;
if (overlayString.equals(OTPApp.MAP_TILE_GOOGLE_HYBRID)) {
mapType = GoogleMap.MAP_TYPE_HYBRID;
} else if (overlayString.equals(OTPApp.MAP_TILE_GOOGLE_NORMAL)) {
mapType = GoogleMap.MAP_TYPE_NORMAL;
} else if (overlayString.equals(OTPApp.MAP_TILE_GOOGLE_TERRAIN)) {
mapType = GoogleMap.MAP_TYPE_TERRAIN;
} else if (overlayString.equals(OTPApp.MAP_TILE_GOOGLE_SATELLITE)) {
mapType = GoogleMap.MAP_TYPE_SATELLITE;
}
mMap.setMapType(mapType);
mMaxZoomLevel = mMap.getMaxZoomLevel();
} else {
if (overlayString.equals(getResources().getString(R.string.tiles_mapnik))) {
mMaxZoomLevel = getResources().getInteger(R.integer.tiles_mapnik_max_zoom);
} else if (overlayString.equals(getResources().getString(R.string.tiles_lyrk))) {
mMaxZoomLevel = getResources().getInteger(R.integer.tiles_lyrk_max_zoom);
tile_width = OTPApp.CUSTOM_MAP_TILE_BIG_WIDTH;
tile_height = OTPApp.CUSTOM_MAP_TILE_BIG_HEIGHT;
} else {
mMaxZoomLevel = getResources().getInteger(R.integer.tiles_maquest_max_zoom);
}
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
CustomUrlTileProvider mTileProvider = new CustomUrlTileProvider(
tile_width,
tile_height, overlayString);
mSelectedTileOverlay = mMap.addTileOverlay(
new TileOverlayOptions().tileProvider(mTileProvider)
.zIndex(OTPApp.CUSTOM_MAP_TILE_Z_INDEX));
if (mMap.getCameraPosition().zoom > mMaxZoomLevel) {
mMap.moveCamera(CameraUpdateFactory.zoomTo(mMaxZoomLevel));
}
}
}
}
/**
* Returns last location coordinates.
* <p>
* This is obtained from the Location Client if it's connected and returns
* a valid Location. If not saved last location is provided.
* <p>
* On successful call to Location Client saved last location is updated.
*
* @return a LatLng object with the most updated user coordinates
*/
public LatLng getLastLocation() {
if (mGoogleApiClient != null) {
if (mGoogleApiClient.isConnected()) {
Location loc = FusedLocationApi.getLastLocation(mGoogleApiClient);
if (loc != null) {
LatLng mCurrentLocation = new LatLng(loc.getLatitude(), loc.getLongitude());
mSavedLastLocation = mCurrentLocation;
return mCurrentLocation;
}
}
if (mSavedLastLocation != null) {
return mSavedLastLocation;
}
}
return null;
}
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (!mMapFailed) {
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
getActivity(),
OTPApp.CONNECTION_FAILURE_RESOLUTION_REQUEST_CODE);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} else {
AlertDialog.Builder errorPlay = new AlertDialog.Builder(getActivity());
errorPlay.setTitle(getResources().getString(R.string.play_services_error_title))
.setMessage(getResources().getString(R.string.play_services_error)
+ connectionResult.getErrorCode())
.setNeutralButton(getResources().getString(android.R.string.ok), null)
.create()
.show();
}
}
}
/**
* Called by Google Play Services when this app is connected
*
* @param connectionHint
*/
@Override
public void onConnected(Bundle connectionHint) {
Location mCurrentLocation = FusedLocationApi.getLastLocation(mGoogleApiClient);
boolean autodetectServerTriggered = false;
if ((!mMapFailed)) {
if (mCurrentLocation != null) {
double savedLatitude = 0;
double savedLongitude = 0;
float distance[] = new float[1];
distance[0] = 0;
if (mSavedLastLocationCheckedForServer != null) {
savedLatitude = mSavedLastLocationCheckedForServer.latitude;
savedLongitude = mSavedLastLocationCheckedForServer.longitude;
}
LatLng mCurrentLatLng = new LatLng(mCurrentLocation.getLatitude(),
mCurrentLocation.getLongitude());
Location.distanceBetween(savedLatitude, savedLongitude, mCurrentLatLng.latitude,
mCurrentLatLng.longitude, distance);
if (!checkServersAreUpdated() || mNewAppVersion) {
runAutoDetectServer(mCurrentLatLng, false);
} else {
if (mNeedToRunAutoDetect) {
autodetectServerTriggered = true;
runAutoDetectServer(mCurrentLatLng, true);
} else if (mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_AUTO_DETECT_SERVER, true)) {
if ((mOTPApp.getSelectedServer() != null)
&& (!LocationUtil
.checkPointInBoundingBox(mCurrentLatLng,
mOTPApp.getSelectedServer()))
&& (((mSavedLastLocationCheckedForServer != null) && (distance[0]
> OTPApp.COORDINATES_IMPORTANT_DIFFERENCE))
|| (mSavedLastLocationCheckedForServer == null))) {
autodetectServerTriggered = true;
runAutoDetectServer(mCurrentLatLng, false);
} else if (mOTPApp.getSelectedServer() == null) {
autodetectServerTriggered = true;
runAutoDetectServer(mCurrentLatLng, true);
}
}
if (!autodetectServerTriggered){
if (mAppStarts) {
Server selectedServer = mOTPApp.getSelectedServer();
if ((selectedServer != null) && selectedServer.areBoundsSet()) {
if (LocationUtil
.checkPointInBoundingBox(mCurrentLatLng, selectedServer)) {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(mCurrentLatLng,
getServerInitialZoom(selectedServer)));
} else {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(getServerCenter(selectedServer),
getServerInitialZoom(selectedServer)));
setMarker(true, getServerCenter(selectedServer), false, true);
}
} else if(selectedServer != null) {
mMap.animateCamera(CameraUpdateFactory
.newLatLngZoom(mCurrentLatLng,
getServerInitialZoom(selectedServer)));
}
}
}
mAppStarts = false;
}
} else if (mOTPApp.getSelectedServer() == null || mNewAppVersion
|| mNeedToUpdateServersList) {
runAutoDetectServerNoLocation(true);
}
}
}
private boolean checkServersAreUpdated() {
ServersDataSource dataSource = ServersDataSource.getInstance(mApplicationContext);
dataSource.open();
boolean result;
Calendar someDaysBefore = Calendar.getInstance();
someDaysBefore.add(Calendar.DAY_OF_MONTH, -OTPApp.EXPIRATION_DAYS_FOR_SERVER_LIST);
Long serversUpdateDate = dataSource.getMostRecentDate();
result = !((serversUpdateDate != null) && (someDaysBefore.getTime().getTime()
> serversUpdateDate));
dataSource.close();
return result;
}
@Override
public void onConnectionSuspended(int i) {
}
/**
* Draws rectangle in the map to represent the bounds, using selected
* server fields for lower left and upper right coordinates.
*
* @param server from which coordinates will be pulled
*/
public void addBoundariesRectangle(Server server) {
List<LatLng> bounds = new ArrayList<LatLng>();
bounds.add(new LatLng(server.getLowerLeftLatitude(), server.getLowerLeftLongitude()));
bounds.add(new LatLng(server.getLowerLeftLatitude(), server.getUpperRightLongitude()));
bounds.add(new LatLng(server.getUpperRightLatitude(), server.getUpperRightLongitude()));
bounds.add(new LatLng(server.getUpperRightLatitude(), server.getLowerLeftLongitude()));
bounds.add(new LatLng(server.getLowerLeftLatitude(), server.getLowerLeftLongitude()));
PolylineOptions boundariesPolylineOptions = new PolylineOptions()
.addAll(bounds)
.color(Color.GRAY);
if (mBoundariesPolyline != null){
mBoundariesPolyline.remove();
}
mBoundariesPolyline = mMap.addPolyline(boundariesPolylineOptions);
}
public float getServerInitialZoom(Server s) {
if (s.isZoomSet()) {
return s.getInitialZoom();
} else {
return OTPApp.defaultInitialZoomLevel;
}
}
public LatLng getServerCenter(Server s) {
if (s.isCenterSet()) {
return new LatLng(s.getCenterLatitude(), s.getCenterLongitude());
} else {
return new LatLng(s.getGeometricalCenterLatitude(), s.getGeometricalCenterLongitude());
}
}
@Override
public void onCameraChange(CameraPosition position) {
if (position.zoom > mMaxZoomLevel && !mMapFailed) {
mMap.moveCamera(CameraUpdateFactory.zoomTo(mMaxZoomLevel));
}
}
@Override
public void onDateComplete(Date tripDate, boolean arriveBy) {
this.mTripDate = tripDate;
this.mArriveBy = arriveBy;
String tripTime = tripDate.toString() + arriveBy;
processRequestTrip();
Log.d(OTPApp.TAG, tripTime);
}
@Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar,
Double minValue, Double maxValue) {
SharedPreferences.Editor editor = mPrefs.edit();
editor.putFloat(OTPApp.PREFERENCE_KEY_LAST_BIKE_TRIANGLE_MIN_VALUE, minValue.floatValue());
editor.putFloat(OTPApp.PREFERENCE_KEY_LAST_BIKE_TRIANGLE_MAX_VALUE, maxValue.floatValue());
editor.commit();
mBikeTriangleMinValue = minValue;
mBikeTriangleMaxValue = maxValue;
String bikeParam = minValue.toString() + maxValue.toString();
Log.d(OTPApp.TAG, bikeParam);
}
@Override
public void onBikeRentalStationListLoad(BikeRentalStationList bikeRentalStationList) {
removeBikeStations();
if (mBtnModeRentedBike.isChecked()){
if (bikeRentalStationList != null){
List<BikeRentalStation> listOfBikeRentalStations = bikeRentalStationList.stations;
if ((listOfBikeRentalStations != null) && !listOfBikeRentalStations.isEmpty()){
mBikeRentalStations = new HashMap<Marker, BikeRentalStationInfo>(listOfBikeRentalStations.size());
MarkerOptions bikeRentalStationMarkerOption = new MarkerOptions();
Drawable drawable = getResources().getDrawable(R.drawable.parking_bicycle);
if (drawable != null) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable.getCurrent();
Bitmap bitmap = bitmapDrawable.getBitmap();
bikeRentalStationMarkerOption.icon(
BitmapDescriptorFactory.fromBitmap(bitmap));
} else {
Log.e(OTPApp.TAG, "Error obtaining drawable to add bike rental icons to the map");
}
for (BikeRentalStation bikeRentalStation : listOfBikeRentalStations){
LatLng position = new LatLng(bikeRentalStation.y,
bikeRentalStation.x);
bikeRentalStationMarkerOption.position(position);
bikeRentalStationMarkerOption.title(bikeRentalStation.name);
bikeRentalStationMarkerOption.snippet(getResources()
.getString(R.string.map_markers_bike_rental_available_bikes) + " " +
bikeRentalStation.bikesAvailable + " | " + getResources()
.getString(R.string.map_markers_bike_rental_available_spaces) + " " +
bikeRentalStation.spacesAvailable);
if (!mMapFailed){
Marker bikeRentalStationMarker = mMap.addMarker(bikeRentalStationMarkerOption);
BikeRentalStationInfo bikeRentalStationInfo = new BikeRentalStationInfo(position,
bikeRentalStation.name);
mBikeRentalStations.put(bikeRentalStationMarker, bikeRentalStationInfo);
}
}
listenForBikeUpdates(true);
}
}
else{
Toast.makeText(mApplicationContext, mApplicationContext.getResources().getString(R.string.toast_bike_rental_load_request_error),
Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onBikeRentalStationListUpdate(BikeRentalStationList bikeRentalStationList) {
if (getActivity() != null){
if (bikeRentalStationList != null) {
List<BikeRentalStation> listOfBikeRentalStations = bikeRentalStationList.stations;
if ((listOfBikeRentalStations != null) && !listOfBikeRentalStations.isEmpty()
&& (mBikeRentalStations != null) && !mBikeRentalStations.isEmpty()) {
for (BikeRentalStation bikeRentalStation : listOfBikeRentalStations) {
for (Map.Entry<Marker, BikeRentalStationInfo> entry : mBikeRentalStations.entrySet()) {
if (entry.getKey().getTitle().equals(bikeRentalStation.name)) {
entry.getKey().setSnippet(getResources()
.getString(R.string.map_markers_bike_rental_available_bikes) + " " +
bikeRentalStation.bikesAvailable + " | " + getResources()
.getString(R.string.map_markers_bike_rental_available_spaces) + " " +
bikeRentalStation.spacesAvailable);
}
if (entry.getKey().isInfoWindowShown()) {
entry.getKey().showInfoWindow();
}
}
}
}
}
else{
Toast.makeText(mApplicationContext, mApplicationContext.getResources().getString(R.string.toast_bike_rental_load_request_error),
Toast.LENGTH_SHORT).show();
listenForBikeUpdates(false);
removeBikeStations();
}
}
}
@Override
public void onBikeRentalStationListFail() {
listenForBikeUpdates(false);
}
public void listenForBikeUpdates(boolean enable){
if (enable){
mApplicationContext.registerReceiver(new AlarmReceiver(), mIntentFilter);
mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME,
OTPApp.DEFAULT_UPDATE_INTERVAL_BIKE_RENTAL,
OTPApp.DEFAULT_UPDATE_INTERVAL_BIKE_RENTAL, mAlarmIntentBikeRentalUpdate);
}
else{
if (!mApplicationContext.getPackageManager()
.queryBroadcastReceivers(mBikeRentalUpdateIntent, mBikeRentalUpdateIntent.getFlags())
.isEmpty()){
mApplicationContext.unregisterReceiver(mAlarmReceiver);
if (!mIsAlarmTripTimeUpdateActive){
mApplicationContext.unregisterReceiver(mAlarmReceiver);
}
}
mAlarmMgr.cancel(mAlarmIntentBikeRentalUpdate);
}
}
public void removeBikeStations(){
if (mBikeRentalStations != null) {
for (Map.Entry<Marker, BikeRentalStationInfo> entry : mBikeRentalStations.entrySet()) {
entry.getKey().remove();
}
mBikeRentalStations = null;
}
}
public void listenForTripTimeUpdates(boolean enable, long timeToStartUpdates){
if (enable){
Calendar calTimeToStartUpdates = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calTimeToStartUpdates.setTime(new Date(timeToStartUpdates));
calTimeToStartUpdates.add(Calendar.HOUR_OF_DAY, -1);
long timeToStartUpdatesProcessed = calTimeToStartUpdates.getTimeInMillis();
if (timeToStartUpdatesProcessed < (System.currentTimeMillis() - 1000)){
timeToStartUpdatesProcessed = System.currentTimeMillis() + 1000;
}
mApplicationContext.registerReceiver(new AlarmReceiver(), mIntentFilter);
mAlarmMgr.setInexactRepeating(AlarmManager.RTC, timeToStartUpdatesProcessed,
OTPApp.DEFAULT_UPDATE_INTERVAL_TRIP_TIME, mAlarmIntentTripTimeUpdate);
}
else{
if (!mApplicationContext.getPackageManager()
.queryBroadcastReceivers(mTripTimeUpdateIntent, mTripTimeUpdateIntent.getFlags())
.isEmpty()){
if (!mIsAlarmBikeRentalUpdateActive){
mApplicationContext.unregisterReceiver(mAlarmReceiver);
}
}
mAlarmMgr.cancel(mAlarmIntentTripTimeUpdate);
}
}
@Override
public void onUpdateTripTimesComplete(HashMap<String, List<TripTimeShort>> timesUpdatesForTrips) {
if (getActivity() != null){
List<Itinerary> itineraries;
if ((itineraries = getFragmentListener().getCurrentItineraryList()) != null){
if ((timesUpdatesForTrips != null) && !timesUpdatesForTrips.isEmpty()){
long lastLegTime = 0;
Leg lastLeg = itineraries.get(0).legs.get(0);
for (Itinerary itinerary : itineraries){
for (Leg leg : itinerary.legs){
long legEndTimeLong = Long.parseLong(leg.startTime);
if (legEndTimeLong > lastLegTime){
lastLegTime = legEndTimeLong;
lastLeg = leg;
}
List<TripTimeShort> tripsTimesUpdates;
if ((tripsTimesUpdates
= timesUpdatesForTrips.get(leg.agencyId + ":" + leg.tripId))
!= null){
TripTimeShort firstStopUpdate = null;
TripTimeShort lastStopUpdate = null;
for (TripTimeShort tripTimeUdapteForStop : tripsTimesUpdates){
if (tripTimeUdapteForStop.stopId.equals(leg.agencyId + ":" + leg.from.stopCode)){
firstStopUpdate = tripTimeUdapteForStop;
}
if (tripTimeUdapteForStop.stopId.equals(leg.agencyId + ":" + leg.to.stopCode)){
lastStopUpdate = tripTimeUdapteForStop;
}
}
if ((firstStopUpdate != null) && (lastStopUpdate != null)){
int legsUpdated = updateLeg(leg, firstStopUpdate, lastStopUpdate);
if (legsUpdated != 0){
for (Map.Entry<Marker, TripInfo> entry : mModeMarkers.entrySet()) {
if (leg.tripId.equals(entry.getValue().getTripId())){
entry.getValue().setSnippet(generateModeMarkerSnippet(leg));
entry.getValue().setDelayInSeconds(leg.departureDelay);
if (entry.getKey().isInfoWindowShown()){
entry.getKey().showInfoWindow();
}
}
}
showNotification(leg, legsUpdated);
}
}
}
}
}
if (isTripOver(lastLeg)){
listenForTripTimeUpdates(false, 0);
}
} else{
Toast.makeText(mApplicationContext,
getResources()
.getString(R.string.toast_realtime_updates_fail),
Toast.LENGTH_SHORT).show();
listenForTripTimeUpdates(false, 0);
}
}
}
}
private boolean isTripOver(Leg leg){
Calendar calTimeToStopUpdates = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calTimeToStopUpdates.setTime(new Date(Long.parseLong(leg.endTime)));
calTimeToStopUpdates.add(Calendar.MILLISECOND, leg.agencyTimeZoneOffset);
Calendar actualTime = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
actualTime.setTime(new Date(System.currentTimeMillis()));
actualTime.add(Calendar.MILLISECOND, leg.agencyTimeZoneOffset);
return actualTime.getTimeInMillis() > calTimeToStopUpdates.getTimeInMillis();
}
/**
* Updates leg fields with departure and arrival new trip times.
*
* @param leg the leg to update
* @param departureTripTimesUpdate departure new trip times
* @param arrivalTripTimesUpdate arrival new trip times
* @return 0 if none are updated, 1 if departure is updated, 2 if arrival is updated, 3 if both
* are updated
*/
private int updateLeg(Leg leg, TripTimeShort departureTripTimesUpdate,
TripTimeShort arrivalTripTimesUpdate){
int updatedLegs = 0;
if (leg.departureDelay != departureTripTimesUpdate.departureDelay){
CharSequence oldDepartureTime = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.startTime), false);
Long scheduledStartTime = Long.parseLong(leg.startTime) - leg.departureDelay * 1000;
leg.departureDelay = departureTripTimesUpdate.departureDelay;
leg.startTime = ((Long)(scheduledStartTime + leg.departureDelay * 1000)).toString();
CharSequence newDepartureTime = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.startTime), false);
if (!oldDepartureTime.equals(newDepartureTime)){
updatedLegs = 1;
}
}
if (leg.arrivalDelay != arrivalTripTimesUpdate.arrivalDelay){
CharSequence oldArrivalTime = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.endTime), false);
Long scheduledEndTime = Long.parseLong(leg.endTime) - leg.arrivalDelay * 1000;
leg.arrivalDelay = arrivalTripTimesUpdate.arrivalDelay;
leg.endTime = ((Long)(scheduledEndTime + leg.arrivalDelay * 1000)).toString();
CharSequence newArrivalTime = ConversionUtils
.getTimeWithContext(mApplicationContext, leg.agencyTimeZoneOffset,
Long.parseLong(leg.endTime), false);
if (!oldArrivalTime.equals(newArrivalTime)){
if (updatedLegs == 1){
updatedLegs = 3;
}
else{
updatedLegs = 2;
}
}
}
return updatedLegs;
}
private String generateDelayText(int delay, boolean longFormat){
String delayText = ConversionUtils.getFormattedDurationTextNoSeconds(delay, longFormat, mApplicationContext);
if (delay == 0){
delayText = getResources()
.getString(R.string.map_markers_warning_live_upates_on_time);
}
else if (delay > 0) {
delayText += " "
+ getResources()
.getString(R.string.map_markers_warning_live_upates_late_arrival);
}
else {
delayText = delayText.replace("-","");
delayText += " "
+ getResources()
.getString(R.string.map_markers_warning_live_upates_early_arrival);
}
return delayText;
}
private void showNotification(Leg leg, int legsUpdated){
if (!getFragmentListener().getCurrentItinerary().contains(leg)){
return;
}
String delayText;
NotificationCompat.InboxStyle inboxStyle =
new NotificationCompat.InboxStyle();
if (legsUpdated == 1){
delayText = generateDelayText(leg.departureDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_origin);
inboxStyle.addLine(generateDelayText(leg.departureDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_origin) + ", "
+ leg.from.name);
}
else if (legsUpdated == 2){
delayText = generateDelayText(leg.arrivalDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_destination);
inboxStyle.addLine(generateDelayText(leg.arrivalDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_destination) +", "
+ leg.to.name);
}
else if (legsUpdated == 3){
if (leg.departureDelay == leg.arrivalDelay){
delayText = generateDelayText(leg.departureDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_origin) + " " +
getResources().getString(R.string.notification_two_delays_connector) + " "
+
getResources().getString(R.string.notification_destination);
inboxStyle.addLine(generateDelayText(leg.departureDelay, true));
inboxStyle.addLine(getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_origin) + ","
+ " " + leg.from.name);
inboxStyle.addLine(getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_destination) + ","
+ " " + leg.to.name);
}
else{
delayText = generateDelayText(leg.departureDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_origin) + " " +
getResources().getString(R.string.notification_two_delays_connector) + " "
+
generateDelayText(leg.arrivalDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ getResources().getString(R.string.notification_destination);
inboxStyle.addLine(generateDelayText(leg.departureDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ leg.from.name);
inboxStyle.addLine( generateDelayText(leg.arrivalDelay, false) + " " +
getResources().getString(R.string.notification_stop_name_conector) + " "
+ leg.to.name);
}
}
else{
return;
}
Intent notificationIntentOpenApp = new Intent(OTPApp.INTENT_NOTIFICATION_ACTION_OPEN_APP);
notificationIntentOpenApp.putExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID, leg.tripId);
PendingIntent notificationOpenAppPendingIntent = PendingIntent
.getBroadcast(mApplicationContext,
0,
notificationIntentOpenApp,
PendingIntent.FLAG_UPDATE_CURRENT);
Intent notificationIntentDismissUpdates = new Intent(OTPApp.INTENT_NOTIFICATION_ACTION_DISMISS_UPDATES);
notificationIntentDismissUpdates.putExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID, leg.tripId);
PendingIntent notificationPendingIntentDismissUpdates = PendingIntent
.getBroadcast(mApplicationContext,
0,
notificationIntentDismissUpdates,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(mApplicationContext)
.setSmallIcon(R.drawable.notification_opentripplanner)
.setContentTitle(ConversionUtils.getRouteShortNameSafe(leg.routeShortName,
leg.routeLongName, mApplicationContext))
.setContentText(delayText)
.setLargeIcon(BitmapFactory
.decodeResource(getActivity()
.getResources(),
DirectionsGenerator
.getNotificationIcon(new TraverseModeSet(leg.mode))))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(notificationOpenAppPendingIntent)
.addAction(R.drawable.ic_action_cancel,
getResources()
.getString(R.string.notification_disable_updates_button),
notificationPendingIntentDismissUpdates);
mBuilder.setStyle(inboxStyle);
NotificationManager notificationManager =
(NotificationManager) mApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = mBuilder.build();
notification.defaults = Notification.DEFAULT_ALL;
notification.flags |= Notification.FLAG_AUTO_CANCEL | Notification.FLAG_SHOW_LIGHTS;
Integer notificationID = Integer.parseInt(leg.tripId);
notificationManager.notify(notificationID, notification);
}
/**
* Checks to see if this version of OTP Android is higher than the last executed version,
* and perform any cleanup necessary.
*/
private void checkAppVersion() {
try {
PackageInfo packageInfo = getActivity().getPackageManager()
.getPackageInfo(getActivity().getPackageName(), 0);
int newVersionCode = packageInfo.versionCode;
int oldVersionCode = mPrefs.getInt(OTPApp.PREFERENCE_KEY_APP_VERSION, newVersionCode);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putInt(OTPApp.PREFERENCE_KEY_APP_VERSION, newVersionCode);
editor.commit();
mNewAppVersion = newVersionCode != oldVersionCode;
/**
* Special handling for introduction of PREFERENCE_KEY_APP_VERSION - see #309
* Otherwise, mNewVersion will be false the first time we execute version_code 13
* when installed as an update to version_code 12.
*/
boolean executedVersion13 = mPrefs.getBoolean(OTPApp.PREFERENCE_KEY_EXECUTED_VERSION_CODE_13, false);
if (mNewAppVersion || !executedVersion13) {
Log.d(OTPApp.TAG, "Updating from app version " + oldVersionCode + " to " +
newVersionCode);
// Erase selected server, so server selection is run after an app update (#309)
editor.putLong(OTPApp.PREFERENCE_KEY_SELECTED_SERVER, 0);
// We've executed version_code 13 or higher once, so set the preference
editor.putBoolean(OTPApp.PREFERENCE_KEY_EXECUTED_VERSION_CODE_13, true);
editor.commit();
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
public void openModeMarker(String tripId){
if (mModeMarkers != null && tripId != null){
for (Map.Entry<Marker, TripInfo> entry : mModeMarkers.entrySet()) {
if (tripId.equals(entry.getValue().getTripId())){
entry.getKey().showInfoWindow();
break;
}
}
}
}
@Override
public void onUpdateTripTimesFail() {
listenForTripTimeUpdates(false, 0);
}
public class AlarmReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
NotificationManager notificationManager;
if (intent.getAction().equals(OTPApp.INTENT_UPDATE_BIKE_RENTAL_ACTION)){
BikeRentalLoad bikeRentalLoad = new BikeRentalLoad(mApplicationContext, false, MainFragment.this);
bikeRentalLoad.execute(mOTPApp.getSelectedServer().getBaseURL());
}
else if (intent.getAction().equals(OTPApp.INTENT_UPDATE_TRIP_TIME_ACTION)){
RequestTimesForTrips requestTimesForTrips =
new RequestTimesForTrips(mApplicationContext, MainFragment.this);
List<String> legsToUpdate = new ArrayList<String>();
for (Itinerary itinerary : getFragmentListener().getCurrentItineraryList()){
for (Leg leg : itinerary.legs){
if (leg.realTime && (TraverseMode.valueOf(leg.mode)).isTransit()){
legsToUpdate.add(leg.agencyId + ":" + leg.tripId);
}
}
}
legsToUpdate.add(0, mOTPApp.getSelectedServer().getBaseURL());
String[] legsToUpdateArray = legsToUpdate.toArray(new String[legsToUpdate.size()]);
requestTimesForTrips.execute(legsToUpdateArray);
}
else if (intent.getAction().equals(OTPApp.INTENT_NOTIFICATION_ACTION_OPEN_APP)){
Intent activityIntent = new Intent(mApplicationContext, MyActivity.class);
activityIntent.setAction(OTPApp.INTENT_NOTIFICATION_RESUME_APP_WITH_TRIP_ID);
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityIntent.putExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID, intent.getStringExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID));
mApplicationContext.startActivity(activityIntent);
}
else if (intent.getAction().equals(OTPApp.INTENT_NOTIFICATION_ACTION_DISMISS_UPDATES)){
notificationManager =
(NotificationManager) mApplicationContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (intent.getStringExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID) != null){
notificationManager.cancelAll();
}
else{
notificationManager.cancel(Integer
.parseInt(intent.getStringExtra(OTPApp.BUNDLE_KEY_INTENT_TRIP_ID)));
}
Toast.makeText(mApplicationContext,
getResources().getString(R.string.notification_disable_updates_info),
Toast.LENGTH_SHORT).show();
listenForTripTimeUpdates(false, 0);
}
}
}
@Override
public void onServerCheckerComplete(String result, boolean isCustomServer, boolean isAutoDetected, boolean isWorking) {
updateSelectedServer(true);
}
public GraphMetadata getmCustomServerMetadata() {
return mCustomServerMetadata;
}
public void setmCustomServerMetadata(GraphMetadata mCustomServerMetadata) {
this.mCustomServerMetadata = mCustomServerMetadata;
}
}